rainbowadn/rainbowadn/flow13/_flowcheque.py

416 lines
14 KiB
Python

import itertools
from typing import Iterable
from rainbowadn.collection.comparison import *
from rainbowadn.collection.keyvalue import *
from rainbowadn.core import *
from rainbowadn.flow.core import *
from rainbowadn.flow.verification.core import *
from rainbowadn.v13 import *
from ._flowiterate import *
from ._flowstandard import *
from ._flowtransaction import *
from ._resolvemapper import *
__all__ = ('FlowCheque',)
class SumReduce(PureReduce[int]):
def pure(self, left: int, right: int) -> int:
return left + right
class ValueMapper(Mapper[HashPoint[FlowCoin], int]):
async def map(self, element: HashPoint[FlowCoin]) -> int:
assert isinstance(element, HashPoint)
return await (await element.resolve()).int_value()
class FlowCheque(StaticMentionable, RecursiveMentionable):
@classmethod
def from_bytes(cls, source: bytes, resolver: HashResolver) -> 'FlowCheque':
return FlowCheque(
FlowStandardFactory.of(FlowTransaction.factory(), HashComparator(Fail())).from_bytes(
source[:HashPoint.HASH_LENGTH], resolver
),
FlowStandardFactory.of(FlowCoin.factory(), HashComparator(Fail())).from_bytes(
source[HashPoint.HASH_LENGTH:2 * HashPoint.HASH_LENGTH], resolver
),
FlowStandardFactory.of(FlowCoin.factory(), HashComparator(Fail())).from_bytes(
source[2 * HashPoint.HASH_LENGTH:3 * HashPoint.HASH_LENGTH], resolver
),
FlowStandardFactory.of(
KeyValue.f(FlowCoin.factory(), FlowTransaction.factory()),
KeyedComparator(HashComparator(Fail()))
).from_bytes(
source[3 * HashPoint.HASH_LENGTH:], resolver
),
)
def points(self) -> Iterable[HashPoint]:
return [*self.transactions.points(), *self.minted.points(), *self.used.points(), *self.usedx.points()]
def __bytes__(self):
return bytes(self.transactions) + bytes(self.minted) + bytes(self.used) + bytes(self.usedx)
def __init__(
self,
transactions: FlowStandard[FlowTransaction],
minted: FlowStandard[FlowCoin],
used: FlowStandard[FlowCoin],
usedx: FlowStandard[KeyValue[FlowCoin, FlowTransaction]],
):
assert isinstance(transactions, FlowStandard)
assert isinstance(minted, FlowStandard)
assert isinstance(used, FlowStandard)
assert isinstance(usedx, FlowStandard)
self.transactions = transactions
self.minted = minted
self.used = used
self.usedx = usedx
@classmethod
async def mint(cls) -> int:
return MINT_CONST
@classmethod
async def total_of(cls, tree: FlowStandard[FlowCoin]) -> int:
assert isinstance(tree, FlowStandard)
reducer: Reducer[HashPoint[FlowCoin]] = await tree.reducer()
assert isinstance(reducer, Reducer)
total: int = await reducer.reduce(MapReduce(ValueMapper(), SumReduce(0)))
assert isinstance(total, int)
return total
async def total_minted(self) -> int:
return await self.total_of(self.minted)
async def total_used(self) -> int:
return await self.total_of(self.used)
async def extra(self) -> int:
mint: int
total_minted: int
total_used: int
mint, total_minted, total_used = await gather(
self.mint(),
self.total_minted(),
self.total_used(),
)
assert isinstance(mint, int)
assert isinstance(total_minted, int)
assert isinstance(total_used, int)
return mint + total_minted - total_used
async def _verify_extra(self) -> bool:
assert (await self.extra()) >= 0
return True
async def _verify_transactions(self) -> bool:
assert_true(
await self.transactions.verify(TransactionVerification(self).loose())
)
return True
async def _verify_minted(self) -> bool:
assert_true(
await self.minted.verify(MintedVerification(self).loose())
)
return True
async def _verify_used(self) -> bool:
assert_true(
await self.used.verify(UsedVerification(self).loose())
)
return True
async def _verify_usedx(self) -> bool:
assert_true(
await self.usedx.verify(UsedXVerification(self).loose())
)
return True
async def verify(self) -> bool:
assert_trues(
await gather(
self._verify_extra(),
self._verify_transactions(),
self._verify_minted(),
self._verify_used(),
self._verify_usedx(),
)
)
return True
@classmethod
async def _transaction_minted(cls, transaction: FlowTransaction) -> Iterable[FlowCoin]:
assert isinstance(transaction, FlowTransaction)
return await FlowIterate.iterate(ResolveMapper.wrap_reducer(await transaction.minted_reducer()), [])
@classmethod
async def _transaction_used(cls, transaction: FlowTransaction) -> Iterable[FlowCoin]:
assert isinstance(transaction, FlowTransaction)
return await FlowIterate.iterate(ResolveMapper.wrap_reducer(await transaction.used_reducer()), [])
@classmethod
async def _transaction_usedx(cls, transaction: FlowTransaction) -> Iterable[KeyValue[FlowCoin, FlowTransaction]]:
assert isinstance(transaction, FlowTransaction)
return (
KeyValue.of(coin, transaction)
for
coin
in
await cls._transaction_used(transaction)
)
@classmethod
async def _make_minted(cls, transactions: Iterable[FlowTransaction]) -> FlowStandard[FlowCoin]:
return await FlowStandardFactory.off(
FlowCoin.factory(),
HashComparator(Fail()),
itertools.chain(
*(await gather(*(cls._transaction_minted(transaction) for transaction in transactions)))
)
)
@classmethod
async def _make_used(cls, transactions: Iterable[FlowTransaction]) -> FlowStandard[FlowCoin]:
return await FlowStandardFactory.off(
FlowCoin.factory(),
HashComparator(Fail()),
itertools.chain(
*(await gather(*(cls._transaction_used(transaction) for transaction in transactions)))
),
)
@classmethod
async def _make_usedx(
cls,
transactions: Iterable[FlowTransaction]
) -> FlowStandard[KeyValue[FlowCoin, FlowTransaction]]:
return await FlowStandardFactory.off(
KeyValue.f(FlowCoin.factory(), FlowTransaction.factory()),
KeyedComparator(HashComparator(Fail())),
itertools.chain(
*(await gather(*(cls._transaction_usedx(transaction) for transaction in transactions)))
)
)
@classmethod
async def make(cls, transactions: Iterable[FlowTransaction]) -> 'FlowCheque':
transactions_standard: FlowStandard[FlowTransaction]
minted: FlowStandard[FlowCoin]
used: FlowStandard[FlowCoin]
usedx: FlowStandard[KeyValue[FlowCoin, FlowTransaction]]
transactions_standard, minted, used, usedx = await gather(
FlowStandardFactory.off(FlowTransaction.factory(), HashComparator(Fail()), transactions),
cls._make_minted(transactions),
cls._make_used(transactions),
cls._make_usedx(transactions),
)
assert isinstance(transactions_standard, FlowStandard)
assert isinstance(minted, FlowStandard)
assert isinstance(used, FlowStandard)
assert isinstance(usedx, FlowStandard)
return cls(
transactions_standard,
minted,
used,
usedx,
)
async def str(self, tab: int) -> str:
assert isinstance(tab, int)
return f'(' \
f'{tabulate(tab + 1)}cheque' \
f'{tabulate(tab + 1)}{await self.transactions.str(tab + 1)}' \
f'{tabulate(tab + 1)}(minted)' \
f'{tabulate(tab + 1)}(used)' \
f'{tabulate(tab + 1)}(usedx)' \
f'{tabulate(tab)})'
class UsedxMapper(Mapper[HashPoint[FlowCoin], HashPoint[KeyValue[FlowCoin, FlowTransaction]]]):
def __init__(self, transaction: HashPoint[FlowTransaction]):
assert isinstance(transaction, HashPoint)
self.transaction = transaction
async def map(self, element: HashPoint[FlowCoin]) -> HashPoint[KeyValue[FlowCoin, FlowTransaction]]:
return HashPoint.of(
await KeyValue.off(
element,
self.transaction
)
)
class TransactionVerification(
Verification[HashPoint[FlowTransaction]]
):
def __init__(self, cheque: FlowCheque):
assert isinstance(cheque, FlowCheque)
self.cheque = cheque
@classmethod
def _usedx_reducer(
cls,
reducer: Reducer[HashPoint[FlowCoin], bool],
transaction: HashPoint[FlowTransaction]
) -> Reducer[HashPoint[KeyValue[FlowCoin, FlowTransaction]], bool]:
assert isinstance(reducer, Reducer)
assert isinstance(transaction, HashPoint)
usedx_reducer: Reducer[HashPoint[KeyValue[FlowCoin, FlowTransaction]], bool] = MapReducer(
UsedxMapper(transaction),
reducer
)
assert isinstance(usedx_reducer, Reducer)
return usedx_reducer
async def _verify_transaction_minted(self, transaction: FlowTransaction) -> bool:
assert isinstance(transaction, FlowTransaction)
assert_true(
await self.cheque.minted.verify_contains_all(
await transaction.minted_reducer()
)
)
return True
async def _verify_transaction_used(self, transaction: FlowTransaction) -> bool:
assert isinstance(transaction, FlowTransaction)
assert_true(
await self.cheque.used.verify_contains_all(
await transaction.used_reducer()
)
)
return True
async def _verify_transaction_usedx(self, transaction: FlowTransaction) -> bool:
assert isinstance(transaction, FlowTransaction)
assert_true(
await self.cheque.usedx.verify_contains_all(
self._usedx_reducer(await transaction.used_reducer(), HashPoint.of(transaction))
)
)
return True
async def verify(self, element: HashPoint[FlowTransaction]) -> bool:
assert isinstance(element, HashPoint)
transaction: FlowTransaction = await element.resolve()
assert isinstance(transaction, FlowTransaction)
assert_trues(
await gather(
self._verify_transaction_minted(transaction),
self._verify_transaction_used(transaction),
self._verify_transaction_usedx(transaction),
transaction.verify(),
)
)
return True
def loose(self) -> Verification[HashPoint[FlowTransaction]]:
return self
class MintedVerification(
Verification[HashPoint[FlowCoin]]
):
def __init__(self, cheque: FlowCheque):
assert isinstance(cheque, FlowCheque)
self.cheque = cheque
async def verify(self, element: HashPoint[FlowCoin]) -> bool:
assert isinstance(element, HashPoint)
assert_true(
await self.cheque.transactions.contains(
(await element.resolve()).transaction,
exact=True
)
)
return True
def loose(self) -> Verification[HashPoint[FlowCoin]]:
return self
class UsedVerification(
Verification[HashPoint[FlowCoin]]
):
def __init__(self, cheque: FlowCheque):
assert isinstance(cheque, FlowCheque)
self.cheque = cheque
async def verify(self, element: HashPoint[FlowCoin]) -> bool:
assert isinstance(element, HashPoint)
assert_true(
await self.cheque.usedx.contains(
HashPoint.of(await KeyValue.off(element, HashPoint.of(FlowTransaction.empty()))),
exact=False
)
)
return True
def loose(self) -> Verification[HashPoint[FlowCoin]]:
return self
class UsedXVerification(
Verification[HashPoint[KeyValue[FlowCoin, FlowTransaction]]]
):
def __init__(self, cheque: FlowCheque):
assert isinstance(cheque, FlowCheque)
self.cheque = cheque
async def _verify_transaction_registred(self, transaction: HashPoint[FlowTransaction]) -> bool:
assert isinstance(transaction, HashPoint)
assert_true(
await self.cheque.transactions.contains(
transaction,
exact=True
)
)
return True
@classmethod
async def _verify_coin_contained_in_transaction(
cls, transaction: HashPoint[FlowTransaction], coin: HashPoint[FlowCoin]
) -> bool:
assert isinstance(transaction, HashPoint)
assert isinstance(coin, HashPoint)
assert_true(
await (await transaction.resolve()).data.in_coins.contains(
coin,
exact=True
)
)
return True
async def _verify_coin_registred_as_used(self, coin: HashPoint[FlowCoin]) -> bool:
assert isinstance(coin, HashPoint)
assert_true(
await self.cheque.used.contains(
coin,
exact=True
)
)
return True
async def _verify(self, pair: KeyValue[FlowCoin, FlowTransaction]):
assert isinstance(pair, KeyValue)
assert_trues(
await gather(
self._verify_transaction_registred(pair.value),
self._verify_coin_contained_in_transaction(pair.value, pair.key),
self._verify_coin_registred_as_used(pair.key),
)
)
return True
async def verify(self, element: HashPoint[KeyValue[FlowCoin, FlowTransaction]]) -> bool:
assert isinstance(element, HashPoint)
assert_true(await self._verify(await element.resolve()))
return True
def loose(self) -> Verification[HashPoint[KeyValue[FlowCoin, FlowTransaction]]]:
return self