416 lines
14 KiB
Python
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
|