diff --git a/plot.py b/plot.py new file mode 100644 index 0000000..85a750a --- /dev/null +++ b/plot.py @@ -0,0 +1,88 @@ +import asyncio +import random + +import matplotlib.pyplot as plt +import numpy as np +from nacl.signing import SigningKey + +from rainbowadn.chain import * +from rainbowadn.core import * +from rainbowadn.nullability import * +from rainbowadn.testing.delayedresolver import DelayedResolver +from rainbowadn.testing.dictresolver import DictResolver +from rainbowadn.testing.instrumentation import Concurrency +from rainbowadn.v13 import * + +plt.rcParams['figure.figsize'] = [12, 6] + +plt.style.use("dark_background") + +plt.subplots_adjust(left=0.05, right=0.99, top=0.99, bottom=0.1) + + +def get_dr() -> ExtendableResolver: + dr = DictResolver() + dr = DelayedResolver(dr, lambda: random.uniform(0.200, 0.500)) + return dr + + +def plot(instrumentation: Concurrency): + plt.plot(*np.array(instrumentation.logs).transpose()) + + +async def plot_every_minute(instrumentation: Concurrency): + while True: + await asyncio.sleep(60) + plot(instrumentation) + plt.show() + plt.clf() + plt.subplots_adjust(left=0.05, right=0.99, top=0.99, bottom=0.1) + + +async def main(): + set_gather_asyncio() + bank: BankChain = BankChain.empty(ReductionChainMetaFactory().loose()) + key_0 = SigningKey.generate() + transaction_0 = Transaction.make( + [], + [CoinData.of(Subject(key_0.verify_key), 100_000)], + [] + ) + coin_0, coin_1 = await transaction_0.coins(MINT_CONST, NotNull(HashPoint.of(Subject(key_0.verify_key)))) + bank = await bank.adds( + [ + transaction_0, + Transaction.make( + [coin_1], + [CoinData.of(Subject(SigningKey.generate().verify_key), 10_000)], + [key_0] + ), + ] + ) + for _ in range(16): + bank = await bank.adds( + [ + Transaction.make( + [], + [CoinData.of(Subject(SigningKey.generate().verify_key), 0)] * 16, + [] + ) + for _ in range(16) + ] + ) + print('built') + assert_true(await bank.verify()) + bank = BankChain.from_reference( + ReductionChainMetaFactory(), await get_dr().migrate_resolved(bank.reference) + ) + print('saved') + set_gather_asyncio() + with Concurrency(DelayedResolver, 'sleep') as sleeps: + # task = asyncio.create_task(plot_every_minute(sleeps)) + assert_true(await bank.verify()) + # task.cancel() + plot(sleeps) + plt.show() + + +asyncio.run(main()) diff --git a/rainbowadn/chain/block.py b/rainbowadn/chain/block.py index a97da96..243c631 100644 --- a/rainbowadn/chain/block.py +++ b/rainbowadn/chain/block.py @@ -37,13 +37,18 @@ class Block(RecursiveMentionable, Generic[HeaderType, StateType]): async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await self.previous.str(tab)}' \ + previous_str, header_str, state_str = await gather( + self.previous.str(tab), + hash_point_format(self.header, tab + 1), + hash_point_format(self.state, tab + 1), + ) + return f'{previous_str}' \ f'{tabulate(tab)}(' \ f'{tabulate(tab + 1)}block' \ f'{tabulate(tab + 1)}(header)' \ - f'{tabulate(tab + 1)}{await hash_point_format(self.header, tab + 1)}' \ + f'{tabulate(tab + 1)}{header_str}' \ f'{tabulate(tab + 1)}(state)' \ - f'{tabulate(tab + 1)}{await hash_point_format(self.state, tab + 1)}' \ + f'{tabulate(tab + 1)}{state_str}' \ f'{tabulate(tab)})' diff --git a/rainbowadn/chain/blockchain.py b/rainbowadn/chain/blockchain.py index 035d4c2..1834f93 100644 --- a/rainbowadn/chain/blockchain.py +++ b/rainbowadn/chain/blockchain.py @@ -1,4 +1,3 @@ -import asyncio from typing import Generic, TypeVar from rainbowadn.core import * @@ -126,8 +125,12 @@ class BlockChain( previous: ChainCollectionInterface[ Block[HeaderType, StateType], HeaderType, ActualStateType ] = await self.previous() - assert_true(await self.verify_link(previous.reference)) - assert_true(await asyncio.create_task(previous.verify())) + assert_trues( + await gather( + self.verify_link(previous.reference), + previous.verify(), + ) + ) return True async def _verify_link( diff --git a/rainbowadn/chain/derivation/activestagestateprotocol.py b/rainbowadn/chain/derivation/activestagestateprotocol.py index 200ebe5..dba3b1e 100644 --- a/rainbowadn/chain/derivation/activestagestateprotocol.py +++ b/rainbowadn/chain/derivation/activestagestateprotocol.py @@ -36,6 +36,30 @@ class ActiveStageStateProtocol( self.stage_factory = stage_factory self.base_state_factory = base_state_factory + async def verify( + self, + previous: NullableReference[ + StateStage[ + HeaderType, + BaseStateType, + StageType + ] + ], + header: HashPoint[HeaderType], + state: HashPoint[ + StateStage[ + HeaderType, + BaseStateType, + StageType + ] + ] + ) -> bool: + return await self.stage_state_protocol().verify( + previous, + header, + state + ) + async def derive( self, previous: NullableReference[ diff --git a/rainbowadn/chain/stages/stagestate.py b/rainbowadn/chain/stages/stagestate.py index 72cc4b9..419f81c 100644 --- a/rainbowadn/chain/stages/stagestate.py +++ b/rainbowadn/chain/stages/stagestate.py @@ -73,17 +73,17 @@ class StageStage( else: previous_stage: StageStage[HeaderType, BaseStateType, StageType] = await self.previous.resolve() assert isinstance(previous_stage, StageStage) - assert_true( - await self.protocol.verify_stage( - previous_stage.stage, - self.stage - ) - ) - assert_true( - await previous_stage.verify( - previous, - header, - base_factory + assert_trues( + await gather( + self.protocol.verify_stage( + previous_stage.stage, + self.stage + ), + previous_stage.verify( + previous, + header, + base_factory + ), ) ) return True @@ -102,8 +102,12 @@ class StageStage( async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await self.previous.str(tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.stage, tab)}' + previous_str, stage_str = await gather( + self.previous.str(tab), + hash_point_format(self.stage, tab) + ) + return f'{previous_str}' \ + f'{tabulate(tab)}{stage_str}' class StageStageFactory(RainbowFactory[StageStage[HeaderType, BaseStateType, StageType]]): @@ -157,17 +161,17 @@ class StateStage( assert isinstance(header, HashPoint) previous_stage: StageStage[HeaderType, BaseStateType, StageType] = await self.previous.resolve() assert isinstance(previous_stage, StageStage) - assert_true( - await self.protocol.verify_state( - previous_stage.stage, - self.state - ) - ) - assert_true( - await previous_stage.verify( - previous, - header, - self.state.factory + assert_trues( + await gather( + self.protocol.verify_state( + previous_stage.stage, + self.state + ), + previous_stage.verify( + previous, + header, + self.state.factory + ) ) ) return True @@ -187,8 +191,12 @@ class StateStage( async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await hash_point_format(self.previous, tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.state, tab)}' + previous_str, state_str = await gather( + hash_point_format(self.previous, tab), + hash_point_format(self.state, tab), + ) + return f'{previous_str}' \ + f'{tabulate(tab)}{state_str}' class StateStageFactory(RainbowFactory[StateStage[HeaderType, BaseStateType, StageType]]): diff --git a/rainbowadn/chain/states/metareductionstateprotocol.py b/rainbowadn/chain/states/metareductionstateprotocol.py index fc632bf..c49cd7b 100644 --- a/rainbowadn/chain/states/metareductionstateprotocol.py +++ b/rainbowadn/chain/states/metareductionstateprotocol.py @@ -11,18 +11,6 @@ StateType = TypeVar('StateType') class MetaReductionStateProtocol(ActiveStateProtocol[HeaderType, StateType]): - async def verify( - self, - previous: NullableReference[StateType], - header: HashPoint[HeaderType], - state: HashPoint[StateType] - ) -> bool: - assert isinstance(previous, NullableReference) - assert isinstance(header, HashPoint) - assert isinstance(state, HashPoint) - assert_eq(state, await self.derive(previous, header)) - return True - def _initial_state(self) -> HashPoint[StateType]: raise NotImplementedError diff --git a/rainbowadn/collection/comparison/keyedcomparator.py b/rainbowadn/collection/comparison/keyedcomparator.py index db482a9..fed9376 100644 --- a/rainbowadn/collection/comparison/keyedcomparator.py +++ b/rainbowadn/collection/comparison/keyedcomparator.py @@ -24,7 +24,15 @@ class KeyedComparator( ) -> Comparison: assert isinstance(original, HashPoint) assert isinstance(key, HashPoint) - return await self.comparator.compare( - (await original.resolve()).key, - (await key.resolve()).key, + original_resolved: Keyed[ComparatorKeyType] + key_resolved: Keyed[ComparatorKeyType] + original_resolved, key_resolved = await gather( + original.resolve(), + key.resolve(), + ) + assert isinstance(original_resolved, Keyed) + assert isinstance(key_resolved, Keyed) + return await self.comparator.compare( + original_resolved.key, + key_resolved.key, ) diff --git a/rainbowadn/collection/comparison/plaincomparator.py b/rainbowadn/collection/comparison/plaincomparator.py index d91c513..77a151c 100644 --- a/rainbowadn/collection/comparison/plaincomparator.py +++ b/rainbowadn/collection/comparison/plaincomparator.py @@ -10,9 +10,13 @@ class PlainComparator(ProtocolComparator[Plain]): async def compare(self, original: HashPoint[Plain], key: HashPoint[Plain]) -> Comparison: assert isinstance(original, HashPoint) assert isinstance(key, HashPoint) - original_value: Plain = await original.resolve() + original_value: Plain + key_value: Plain + original_value, key_value = await gather( + original.resolve(), + key.resolve(), + ) assert isinstance(original_value, Plain) - key_value: Plain = await key.resolve() assert isinstance(key_value, Plain) if key_value.source < original_value.source: return Left() diff --git a/rainbowadn/collection/keymetadata.py b/rainbowadn/collection/keymetadata.py index 2f585f0..9317abd 100644 --- a/rainbowadn/collection/keymetadata.py +++ b/rainbowadn/collection/keymetadata.py @@ -27,8 +27,12 @@ class KeyMetadata(Keyed[ActiveKeyType], Generic[ActiveKeyType, MetaDataType]): async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await hash_point_format(self.key, tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.metadata, tab)}' + key_str, metadata_str = await gather( + hash_point_format(self.key, tab), + hash_point_format(self.metadata, tab) + ) + return f'{key_str}' \ + f'{tabulate(tab)}{metadata_str}' class KeyMetadataFactory( diff --git a/rainbowadn/collection/linear/array.py b/rainbowadn/collection/linear/array.py index 3e0d573..010ab56 100644 --- a/rainbowadn/collection/linear/array.py +++ b/rainbowadn/collection/linear/array.py @@ -30,8 +30,14 @@ class Array(RecursiveMentionable, Generic[ElementType]): async def str(self, tab: int) -> str: assert isinstance(tab, int) formatted = f'(' - for hash_point in self.array: - formatted += f'{tabulate(tab + 1)}{await hash_point_format(hash_point, tab + 1)}' + formatted += ''.join( + f'{tabulate(tab + 1)}{hash_point_str}' + for hash_point_str in gather( + *( + hash_point_format(hash_point, tab + 1) for hash_point in self.array + ) + ) + ) return f'{formatted}' \ f'{tabulate(tab)})' diff --git a/rainbowadn/collection/linear/stack.py b/rainbowadn/collection/linear/stack.py index db04f0b..8db076b 100644 --- a/rainbowadn/collection/linear/stack.py +++ b/rainbowadn/collection/linear/stack.py @@ -31,8 +31,12 @@ class Stack(RecursiveMentionable, Generic[ElementType]): async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await self.previous.str(tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.element, tab)}' + previous_str, element_str = await gather( + self.previous.str(tab), + hash_point_format(self.element, tab), + ) + return f'{previous_str}' \ + f'{tabulate(tab)}{element_str}' @classmethod def of( @@ -76,6 +80,17 @@ class Stack(RecursiveMentionable, Generic[ElementType]): async for element in cls.iter(stack.previous): yield element + @classmethod + async def list( + cls, + reference: NullableReference['Stack[ElementType]'] + ) -> list[ElementType]: + return list( + await gather( + *[element.resolve() async for element in cls.iter(reference)] + ) + ) + class StackFactory(RainbowFactory[Stack[ElementType]], Generic[ElementType]): def __init__(self, factory: RainbowFactory[ElementType]): diff --git a/rainbowadn/collection/pair.py b/rainbowadn/collection/pair.py index 29df9f0..ab2b931 100644 --- a/rainbowadn/collection/pair.py +++ b/rainbowadn/collection/pair.py @@ -29,9 +29,13 @@ class Pair( async def str(self, tab: int) -> str: assert isinstance(tab, int) + e0_str, e1_str = await gather( + hash_point_format(self.element0, tab), + hash_point_format(self.element1, tab), + ) return f'(pair)' \ - f'{tabulate(tab)}{await hash_point_format(self.element0, tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.element1, tab)}' + f'{tabulate(tab)}{e0_str}' \ + f'{tabulate(tab)}{e1_str}' class PairFactory( diff --git a/rainbowadn/collection/trees/binary/avl.py b/rainbowadn/collection/trees/binary/avl.py index 6329f1c..dee2495 100644 --- a/rainbowadn/collection/trees/binary/avl.py +++ b/rainbowadn/collection/trees/binary/avl.py @@ -23,13 +23,17 @@ class AVL(BinaryBalancing[ActiveKeyType, Integer, TreeType]): creation: BinaryCreation[ActiveKeyType, Integer, TreeType] ) -> HashPoint[Integer]: assert isinstance(creation, BinaryCreation) + height_l, height_r = await gather( + self.height(BinaryProtocolized(creation, treel)), + self.height(BinaryProtocolized(creation, treer)), + ) return HashPoint.of( Integer( 1 + max( - await self.height(BinaryProtocolized(creation, treel)), - await self.height(BinaryProtocolized(creation, treer)) + height_l, + height_r, ) ) ) @@ -80,7 +84,11 @@ class BalanceAction( self, case: ProtocolizedBinarySplit[ActiveKeyType, Integer, TreeType] ) -> TreeType: - delta = (await AVL.height(case.protocolizedl())) - (await AVL.height(case.protocolizedr())) + height_l, height_r = await gather( + AVL.height(case.protocolizedl()), + AVL.height(case.protocolizedr()), + ) + delta = height_l - height_r assert isinstance(delta, int) if delta < -1: return await self.on_symmetric(InnerOuter(case.protocol), case.split) @@ -98,15 +106,19 @@ class BalanceAction( assert isinstance(symmetry, Symmetric) assert isinstance(split, BinarySplit) splito = await symmetry.protocol.fsplit(symmetry.outer(split)) - if ( - (await AVL.height(symmetry.protocolizedi(splito))) - > - (await AVL.height(symmetry.protocolizedo(splito))) - ): + height_oi, height_oo = await gather( + AVL.height(symmetry.protocolizedi(splito)), + AVL.height(symmetry.protocolizedo(splito)), + ) + if height_oi > height_oo: splitoi = await symmetry.protocol.fsplit(symmetry.inner(splito)) + inner, outer = await gather( + symmetry.tree(symmetry.inner(split), symmetry.inner(splitoi), split.key), + symmetry.tree(symmetry.outer(splitoi), symmetry.outer(splito), splito.key), + ) return await symmetry.tree( - await symmetry.tree(symmetry.inner(split), symmetry.inner(splitoi), split.key), - await symmetry.tree(symmetry.outer(splitoi), symmetry.outer(splito), splito.key), + inner, + outer, splitoi.key ) else: diff --git a/rainbowadn/collection/trees/binary/binarytree.py b/rainbowadn/collection/trees/binary/binarytree.py index 321aa87..adb191c 100644 --- a/rainbowadn/collection/trees/binary/binarytree.py +++ b/rainbowadn/collection/trees/binary/binarytree.py @@ -37,9 +37,14 @@ class BinaryTree(RecursiveMentionable, Generic[TreeKeyType]): async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await self.treel.str(tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.key, tab)}' \ - f'{tabulate(tab)}{await self.treer.str(tab)}' + treel_str, key_str, treer_str = await gather( + self.treel.str(tab), + hash_point_format(self.key, tab), + self.treer.str(tab), + ) + return f'{treel_str}' \ + f'{tabulate(tab)}{key_str}' \ + f'{tabulate(tab)}{treer_str}' class BinaryTreeFactory(RainbowFactory[BinaryTree[TreeKeyType]], Generic[TreeKeyType]): diff --git a/rainbowadn/collection/trees/binary/core/balancedcreation.py b/rainbowadn/collection/trees/binary/core/balancedcreation.py index ad9b605..71a2b26 100644 --- a/rainbowadn/collection/trees/binary/core/balancedcreation.py +++ b/rainbowadn/collection/trees/binary/core/balancedcreation.py @@ -31,12 +31,16 @@ class BalancedCreation( async def tree(self, treel: TreeType, treer: TreeType, key: HashPoint[ActiveKeyType]) -> TreeType: assert isinstance(key, HashPoint) + balancedl, balancedr = await gather( + self.protocol.balance(BinaryProtocolized(self, treel)), + self.protocol.balance(BinaryProtocolized(self, treer)), + ) return await self.protocol.balance( BinaryProtocolized( self, await self._tree( - await self.protocol.balance(BinaryProtocolized(self, treel)), - await self.protocol.balance(BinaryProtocolized(self, treer)), + balancedl, + balancedr, key ) ) diff --git a/rainbowadn/core/__init__.py b/rainbowadn/core/__init__.py index ad61c0e..5648f70 100644 --- a/rainbowadn/core/__init__.py +++ b/rainbowadn/core/__init__.py @@ -1,5 +1,6 @@ from .asserts import * from .extendableresolver import ExtendableResolver +from .gather import * from .hash_point_format import * from .hashpoint import HashPoint from .hashresolver import HashResolver diff --git a/rainbowadn/core/asserts.py b/rainbowadn/core/asserts.py index 051c862..9137b52 100644 --- a/rainbowadn/core/asserts.py +++ b/rainbowadn/core/asserts.py @@ -1,6 +1,6 @@ -from typing import Optional, TypeVar +from typing import Iterable, Optional, TypeVar -__all__ = ('assert_true', 'assert_false', 'assert_none', 'assert_eq',) +__all__ = ('assert_true', 'assert_trues', 'assert_false', 'assert_none', 'assert_eq',) def assert_true(value: bool) -> bool: @@ -8,6 +8,12 @@ def assert_true(value: bool) -> bool: return True +def assert_trues(values: Iterable[bool]) -> bool: + for value in values: + assert_true(value) + return True + + def assert_false(value: bool) -> bool: assert value is False return True diff --git a/rainbowadn/core/gather.py b/rainbowadn/core/gather.py new file mode 100644 index 0000000..373a10f --- /dev/null +++ b/rainbowadn/core/gather.py @@ -0,0 +1,35 @@ +import asyncio +from typing import AsyncIterable, TypeVar + +__all__ = ('gather', 'asum', 'alist', 'set_gather_asyncio', 'set_gather_linear',) + +_gather = asyncio.gather + + +async def _local_gather(*args): + return [await arg for arg in args] + + +def set_gather_asyncio(): + global _gather + _gather = asyncio.gather + + +def set_gather_linear(): + global _gather + _gather = _local_gather + + +def gather(*args): + return _gather(*args) + + +async def asum(iterable): + return sum(await gather(*iterable)) + + +T = TypeVar('T') + + +async def alist(aiterable: AsyncIterable[T]) -> list[T]: + return [x async for x in aiterable] diff --git a/rainbowadn/encryption/encrypted.py b/rainbowadn/encryption/encrypted.py index a07a5ec..dc79f21 100644 --- a/rainbowadn/encryption/encrypted.py +++ b/rainbowadn/encryption/encrypted.py @@ -40,14 +40,12 @@ class Encrypted(RecursiveMentionable, Generic[EncryptedType]): async def encrypt(cls, decrypted: EncryptedType, key: bytes) -> 'Encrypted[EncryptedType]': assert isinstance(key, bytes) hashpoints = tuple(decrypted.points()) if isinstance(decrypted, RecursiveMentionable) else () - resolution: tuple[HashPoint[Encrypted[EncryptedType]], ...] = tuple( - [ - await cls.encrypt_hashpoint(hash_point, key) - for - hash_point - in - hashpoints - ] + resolution = tuple( + await gather( + *( + cls.encrypt_hashpoint(hash_point, key) for hash_point in hashpoints + ) + ) ) return cls.construct( key, diff --git a/rainbowadn/testing/delayedresolver.py b/rainbowadn/testing/delayedresolver.py new file mode 100644 index 0000000..d925f48 --- /dev/null +++ b/rainbowadn/testing/delayedresolver.py @@ -0,0 +1,29 @@ +import asyncio +from typing import Callable, TypeVar + +from rainbowadn.core import * + +__all__ = ('DelayedResolver',) + +Mentioned = TypeVar('Mentioned') + + +class DelayedResolver(ExtendableResolver): + def __init__(self, resolver: HashResolver, delay: Callable[[], float]): + self.resolver = resolver + self.delay = delay + + async def sleep(self): + await asyncio.sleep(self.delay()) + + async def resolve(self, point: bytes) -> tuple[bytes, 'HashResolver']: + assert isinstance(point, bytes) + await self.sleep() + resolved, resolver = await self.resolver.resolve(point) + return resolved, DelayedResolver(resolver, self.delay) + + async def extend(self, hash_point: HashPoint[Mentioned]) -> ExtendableResolver: + if isinstance(self.resolver, ExtendableResolver): + return DelayedResolver(await self.resolver.extend(hash_point), self.delay) + else: + raise TypeError diff --git a/rainbowadn/testing/dictresolver.py b/rainbowadn/testing/dictresolver.py index b70df71..60eb8dc 100644 --- a/rainbowadn/testing/dictresolver.py +++ b/rainbowadn/testing/dictresolver.py @@ -25,8 +25,9 @@ class DictResolver(ExtendableResolver): assert isinstance(value, Mentionable) self.table[hash_point.point] = HashPoint.bytes_of_mentioned(value) if isinstance(value, RecursiveMentionable): - for hash_point in value.points(): - await self.save(hash_point) + await gather( + *(self.save(hp) for hp in value.points()) + ) async def extend(self, hash_point: HashPoint[Mentioned]) -> 'ExtendableResolver': await self.save(hash_point) diff --git a/rainbowadn/testing/instrumentation.py b/rainbowadn/testing/instrumentation.py index cd98499..21ea498 100644 --- a/rainbowadn/testing/instrumentation.py +++ b/rainbowadn/testing/instrumentation.py @@ -1,7 +1,14 @@ import functools +import time +from typing import Generic, TypeVar + +__all__ = ('Instrumentation', 'Counter', 'Concurrency',) -class Instrumentation: +IType = TypeVar('IType') + + +class Instrumentation(Generic[IType]): deinstrumentation = {} def __init__(self, target, methodname: str): @@ -11,7 +18,7 @@ class Instrumentation: def instrument(self, method, *args, **kwargs): raise NotImplementedError - def __enter__(self): + def __enter__(self: IType) -> IType: assert not hasattr(self, 'method') self.method = getattr(self.target, self.methodname) @@ -23,6 +30,8 @@ class Instrumentation: setattr(self.target, self.methodname, self.wrap) + return self + def schedule_deinstrumentation(self): self.deinstrumentation[self.wrap] = self.method del self.wrap @@ -45,3 +54,25 @@ class Counter(Instrumentation): def instrument(self, method, *args, **kwargs): self.counter += 1 return method(*args, **kwargs) + + +class Concurrency(Instrumentation): + start = time.time() + + def __init__(self, target, methodname: str): + super().__init__(target, methodname) + self.concurrency = 0 + self.logs: list[tuple[float, int]] = [] + + def time(self) -> float: + return time.time() - self.start + + async def instrument(self, method, *args, **kwargs): + self.logs.append((self.time(), self.concurrency)) + self.concurrency += 1 + self.logs.append((self.time(), self.concurrency)) + result = await method(*args, **kwargs) + self.logs.append((self.time(), self.concurrency)) + self.concurrency -= 1 + self.logs.append((self.time(), self.concurrency)) + return result diff --git a/rainbowadn/testing/test_all.py b/rainbowadn/testing/test_all.py index a6133a3..89f7428 100644 --- a/rainbowadn/testing/test_all.py +++ b/rainbowadn/testing/test_all.py @@ -16,16 +16,22 @@ from rainbowadn.encryption import * from rainbowadn.nullability import * from rainbowadn.v13 import * from rainbowadn.wrisbt import * +from .delayedresolver import DelayedResolver from .dictresolver import DictResolver -from .instrumentation import Counter +from .instrumentation import * class TestAll(unittest.IsolatedAsyncioTestCase): """examples rather than real tests""" + @classmethod + def dr(cls) -> ExtendableResolver: + dr = DictResolver() + dr = DelayedResolver(dr, lambda: 0.000) + return dr + async def test_bankchain(self): - with self.subTest('setup'): - dr = DictResolver() + set_gather_asyncio() with self.subTest('create empty'): bank: BankChain = BankChain.empty(ReductionChainMetaFactory().loose()) with self.subTest('prepare transactions'): @@ -51,19 +57,22 @@ class TestAll(unittest.IsolatedAsyncioTestCase): bank = await bank.adds( [] ) - print(bank.reference.str(0)) + print(await bank.reference.str(0)) with self.subTest('verify'): assert_true(await bank.verify()) with self.subTest('recover'): - await dr.save(HashPoint.of(bank.reference)) bank = BankChain.from_reference( - ReductionChainMetaFactory(), await dr.migrate_resolved(bank.reference) + ReductionChainMetaFactory(), await self.dr().migrate_resolved(bank.reference) ) - print(bank.reference.str(0)) + set_gather_asyncio() + print('recovering') + print(await bank.reference.str(0)) + print('recovered') with self.subTest('verify'): assert_true(await bank.verify()) async def test_wrisbt(self): + set_gather_linear() with self.subTest('setup'): stoptime = time.process_time() @@ -75,7 +84,6 @@ class TestAll(unittest.IsolatedAsyncioTestCase): stoptime = now return delta - dr = DictResolver() n = 2500 keysize = 7 with self.subTest('create empty'): @@ -89,10 +97,10 @@ class TestAll(unittest.IsolatedAsyncioTestCase): assert_true(await btree.contains(key)) measure('add') with self.subTest('save'): - await dr.save(HashPoint.of(btree)) + btree = await self.dr().migrate_resolved(btree) measure('save') + set_gather_asyncio() with self.subTest('resolve and iterate'): - btree = await dr.migrate_resolved(btree) assert_eq(len(await btree.keys()), n) print(btree.height) measure('resolve and iterate') @@ -106,13 +114,14 @@ class TestAll(unittest.IsolatedAsyncioTestCase): measure('resolve and add') async def test_wrisbt_index(self): + set_gather_linear() with self.subTest('create empty'): factory: RainbowFactory[Pair[Plain, Plain]] = PairFactory(Plain.factory(), Plain.factory()).loose() chain: ChainCollectionInterface[Any, Pair[Plain, Plain], WrisbtRoot] = BlockChainFactory( WrisbtChainProtocol(factory, 2).loose() ).empty().loose() with self.subTest('fill'): - for _ in range(1000): + for _ in range(100): chain = await chain.add( HashPoint.of( Pair( @@ -122,6 +131,7 @@ class TestAll(unittest.IsolatedAsyncioTestCase): ) ) with self.subTest('check'): + set_gather_asyncio() assert_true(await chain.verify()) with self.subTest('measure height'): reference = await chain.actual_state() @@ -129,6 +139,7 @@ class TestAll(unittest.IsolatedAsyncioTestCase): print((await reference.resolve()).height) async def test_avl(self): + set_gather_linear() tree: ActiveBinaryTree[Plain, Integer] = ActiveBinaryTree.empty( AVL(PlainComparator(Replace())), Plain.factory() ) @@ -137,6 +148,7 @@ class TestAll(unittest.IsolatedAsyncioTestCase): print(await tree.reference.str(0)) async def test_avl_stress(self): + set_gather_linear() protocol = AVL(PlainComparator(Replace())) tree: ActiveBinaryTree[Plain, Integer] = ActiveBinaryTree.empty( protocol, Plain.factory() @@ -146,10 +158,10 @@ class TestAll(unittest.IsolatedAsyncioTestCase): print(await AVL.height(tree.protocolized())) async def test_encryption(self): + set_gather_asyncio() instrumentation = Counter(Encrypted, 'encrypt') with self.subTest('setup'): key = b'a' * 32 - dr = DictResolver() with self.subTest('create empty'): tree: ActiveBinaryTree[Plain, Integer] = ActiveBinaryTree.empty( AVL(PlainComparator(Replace())), Plain.factory() @@ -174,8 +186,7 @@ class TestAll(unittest.IsolatedAsyncioTestCase): with instrumentation: eeed = await Encrypted.encrypt(target, key) print(instrumentation.counter) - await dr.save(HashPoint.of(eeed)) - print(await (await dr.migrate_resolved(eeed)).decrypted.str(0)) + print(await (await self.dr().migrate_resolved(eeed)).decrypted.str(0)) with self.subTest('re-encrypt'): new_key = b'b' * 32 target = eeed.decrypted diff --git a/rainbowadn/v13/bankprotocol.py b/rainbowadn/v13/bankprotocol.py index f1f8903..da91fc8 100644 --- a/rainbowadn/v13/bankprotocol.py +++ b/rainbowadn/v13/bankprotocol.py @@ -18,9 +18,14 @@ class BankProtocol(ReductionProtocol[NullableReference[Stack[Transaction]], Bank reduce: Reducible[NullableReference[Stack[Transaction]], BankState] ) -> ReductionResult[NullableReference[Stack[Transaction]], BankState]: assert isinstance(reduce, Reducible) - bank_state: BankState = await reduce.accumulator.resolve() + bank_state: BankState + reference: NullableReference[Stack[Transaction]] + bank_state, reference = await gather( + reduce.accumulator.resolve(), + reduce.reductor.resolve(), + ) assert isinstance(bank_state, BankState) - reference: NullableReference[Stack[Transaction]] = await reduce.reductor.resolve() + assert isinstance(reference, NullableReference) if reference.null(): return Reduced(HashPoint.of(bank_state.without_miner())) else: diff --git a/rainbowadn/v13/bankstate.py b/rainbowadn/v13/bankstate.py index 386cf3e..f43ffdd 100644 --- a/rainbowadn/v13/bankstate.py +++ b/rainbowadn/v13/bankstate.py @@ -90,23 +90,41 @@ class BankState(RecursiveMentionable, StaticMentionable): minted: ActiveBinaryTree[Coin, Integer] = self.minted_tree() used: ActiveBinaryTree[Coin, Integer] = self.used_tree() async for in_coin in coins: - assert_true(await minted.contains(in_coin)) - assert_false(await used.contains(in_coin)) - used = await used.add(in_coin) + minted_contains, used_contains, used = await gather( + minted.contains(in_coin), + used.contains(in_coin), + used.add(in_coin) + ) + assert_true(minted_contains) + assert_false(used_contains) + assert isinstance(used, ActiveBinaryTree) return BankState(self.minted, used.reference, self.miner, self.length) + async def _mint_coins( + self, + transaction: Transaction + ) -> 'BankState': + miner = self.miner_nullable() + minted: ActiveBinaryTree[Coin, Integer] = self.minted_tree() + async for coin, miner in transaction.iter_coins(self.mint(), miner): + minted_contains, minted = await gather( + minted.contains(HashPoint.of(coin)), + minted.add(HashPoint.of(coin)), + ) + assert_false(minted_contains) + assert isinstance(minted, ActiveBinaryTree) + return BankState(minted.reference, self.used, NullableReference(miner, self.miner.factory), self.length) + async def mint_coins( self, transaction: Transaction ) -> 'BankState': - assert_true(await self.verify(transaction)) - miner = self.miner_nullable() - minted: ActiveBinaryTree[Coin, Integer] = self.minted_tree() - async for coin, miner in transaction.iter_coins(self.mint(), miner): - assert_false(await minted.contains(HashPoint.of(coin))) - minted = await minted.add(HashPoint.of(coin)) - assert isinstance(minted, ActiveBinaryTree) - return BankState(minted.reference, self.used, NullableReference(miner, self.miner.factory), self.length) + verified, bank_state = await gather( + self.verify(transaction), + self._mint_coins(transaction), + ) + assert_true(verified) + return bank_state def miner_nullable(self) -> Nullable[HashPoint[Subject]]: return self.miner.reference @@ -137,14 +155,20 @@ class BankState(RecursiveMentionable, StaticMentionable): async def str(self, tab: int) -> str: assert isinstance(tab, int) + miner_str, minted_str, used_str, length_str = await gather( + self.miner.str(tab + 1), + self.minted.str(tab + 1), + self.used.str(tab + 1), + hash_point_format(self.length, tab + 1), + ) return f'(' \ f'{tabulate(tab + 1)}bank' \ f'{tabulate(tab + 1)}(miner)' \ - f'{tabulate(tab + 1)}{await self.miner.str(tab + 1)}' \ + f'{tabulate(tab + 1)}{miner_str}' \ f'{tabulate(tab + 1)}(minted)' \ - f'{tabulate(tab + 1)}{await self.minted.str(tab + 1)}' \ + f'{tabulate(tab + 1)}{minted_str}' \ f'{tabulate(tab + 1)}(used)' \ - f'{tabulate(tab + 1)}{await self.used.str(tab + 1)}' \ + f'{tabulate(tab + 1)}{used_str}' \ f'{tabulate(tab + 1)}(length)' \ - f'{tabulate(tab + 1)}{await hash_point_format(self.length, tab + 1)}' \ + f'{tabulate(tab + 1)}{length_str}' \ f'{tabulate(tab)})' diff --git a/rainbowadn/v13/transaction.py b/rainbowadn/v13/transaction.py index 8225871..65dea9c 100644 --- a/rainbowadn/v13/transaction.py +++ b/rainbowadn/v13/transaction.py @@ -49,8 +49,12 @@ class CoinData(RecursiveMentionable, StaticMentionable): async def str(self, tab: int) -> str: assert isinstance(tab, int) - return f'{await hash_point_format(self.owner, tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.value, tab)}' + owner_str, value_str = await gather( + hash_point_format(self.owner, tab), + hash_point_format(self.value, tab), + ) + return f'{owner_str}' \ + f'{tabulate(tab)}{value_str}' class Coin(RecursiveMentionable, StaticMentionable): @@ -90,13 +94,23 @@ class Coin(RecursiveMentionable, StaticMentionable): async def str(self, tab: int) -> str: assert isinstance(tab, int) + data_str, index_str = await gather( + hash_point_format(self.data, tab + 1), + hash_point_format(self.index, tab + 1), + ) return f'(' \ f'{tabulate(tab + 1)}coin' \ - f'{tabulate(tab + 1)}{await hash_point_format(self.data, tab + 1)}' \ + f'{tabulate(tab + 1)}{data_str}' \ f'{tabulate(tab + 1)}(origin)' \ - f'{tabulate(tab + 1)}{await hash_point_format(self.index, tab + 1)}' \ + f'{tabulate(tab + 1)}{index_str}' \ f'{tabulate(tab)})' + async def int_value(self) -> int: + return await (await self.data_resolved()).int_value() + + async def owner_resolved(self) -> Subject: + return await (await self.data_resolved()).owner.resolve() + class TransactionData(RecursiveMentionable, StaticMentionable): def __init__( @@ -130,38 +144,57 @@ class TransactionData(RecursiveMentionable, StaticMentionable): ).from_bytes(source[HashPoint.HASH_LENGTH:], resolver), ) + async def _signature_verify(self, coin: Coin, signature: Signature) -> bool: + assert_true( + signature.verify( + await coin.owner_resolved(), + self.hash_point + ) + ) + return True + async def _verify_signatures( self, signatures: NullableReference[Stack[Signature]] ) -> bool: - for coin, signature in zip( - [x async for x in self.iter_in_coins_resolved()], - [x async for x in Stack.iter(signatures)], - strict=True - ): - assert_true( - (await signature.resolve()).verify( - await (await coin.data_resolved()).owner.resolve(), - self.hash_point - ) + assert_trues( + await gather( + *[ + self._signature_verify(coin, signature) + for + coin, signature + in + zip( + await self.in_coins_resolved(), + await Stack.list(signatures), + strict=True + ) + ] ) + ) return True def iter_in_coins(self) -> AsyncIterable[HashPoint[Coin]]: return Stack.iter(self.in_coins) - async def iter_in_coins_resolved(self) -> AsyncIterable[Coin]: - async for coin in self.iter_in_coins(): - yield await coin.resolve() + async def in_coins_resolved(self) -> list[Coin]: + return await Stack.list(self.in_coins) async def _total_in(self) -> int: - return sum([await (await coin.data_resolved()).int_value() async for coin in self.iter_in_coins_resolved()]) + return await asum( + coin.int_value() for coin in await self.in_coins_resolved() + ) def iter_out_coins(self) -> AsyncIterable[HashPoint[CoinData]]: return Stack.iter(self.out_coins) + async def out_coins_resolved(self) -> list[CoinData]: + return await Stack.list(self.out_coins) + async def _total_out(self) -> int: - return sum([await (await coin.resolve()).int_value() async for coin in self.iter_out_coins()]) + return await asum( + coin.int_value() for coin in await self.out_coins_resolved() + ) async def _verify_values(self, mint: int) -> bool: assert isinstance(mint, int) @@ -170,7 +203,11 @@ class TransactionData(RecursiveMentionable, StaticMentionable): async def extra(self, mint: int) -> int: assert isinstance(mint, int) - return (await self._total_in()) + mint - (await self._total_out()) + total_in, total_out = await gather( + self._total_in(), + self._total_out(), + ) + return total_in + mint - total_out async def verify( self, @@ -179,16 +216,24 @@ class TransactionData(RecursiveMentionable, StaticMentionable): ) -> bool: assert isinstance(signatures, NullableReference) assert isinstance(mint, int) - assert_true(await self._verify_signatures(signatures)) - assert_true(await self._verify_values(mint)) + assert_trues( + await gather( + self._verify_signatures(signatures), + self._verify_values(mint), + ) + ) return True async def str(self, tab: int) -> str: assert isinstance(tab, int) + in_str, out_str = await gather( + self.in_coins.str(tab), + self.out_coins.str(tab), + ) return f'(in)' \ - f'{tabulate(tab)}{await self.in_coins.str(tab)}' \ + f'{tabulate(tab)}{in_str}' \ f'{tabulate(tab)}(out)' \ - f'{tabulate(tab)}{await self.out_coins.str(tab)}' + f'{tabulate(tab)}{out_str}' class Transaction(RecursiveMentionable, StaticMentionable): @@ -276,10 +321,14 @@ class Transaction(RecursiveMentionable, StaticMentionable): async def str(self, tab: int) -> str: assert isinstance(tab, int) + data_str, signatures_str = await gather( + hash_point_format(self.data, tab + 1), + self.signatures.str(tab + 1), + ) return f'(' \ f'{tabulate(tab + 1)}transaction' \ - f'{tabulate(tab + 1)}{hash_point_format(self.data, tab + 1)}' \ - f'{tabulate(tab + 1)}{self.signatures.str(tab + 1)}' \ + f'{tabulate(tab + 1)}{data_str}' \ + f'{tabulate(tab + 1)}{signatures_str}' \ f'{tabulate(tab)})' @classmethod diff --git a/rainbowadn/wrisbt/weakreferenceindexsetbtree.py b/rainbowadn/wrisbt/weakreferenceindexsetbtree.py index c6050ca..6496ae7 100644 --- a/rainbowadn/wrisbt/weakreferenceindexsetbtree.py +++ b/rainbowadn/wrisbt/weakreferenceindexsetbtree.py @@ -156,8 +156,13 @@ class WeakReferenceIndexSetBTree(RecursiveMentionable): f'{tabulate(tab)}(' for key_index in range(self.keys): formatted += f'{tabulate(tab + 1)}{self.key_no(key_index).hex()}' - for child_index in range(self.children): - formatted += f'{tabulate(tab + 1)}{await hash_point_format(self.child_no(child_index), tab + 1)}' + formatted += ''.join( + f'{tabulate(tab + 1)}{child_formatted}' for child_formatted in ( + await gather( + hash_point_format(self.child_no(child_index), tab + 1) for child_index in range(self.children) + ) + ) + ) return f'{formatted}' \ f'{tabulate(tab)})' diff --git a/rainbowadn/wrisbt/wrisbtindex.py b/rainbowadn/wrisbt/wrisbtindex.py index dd7e041..2eb08f9 100644 --- a/rainbowadn/wrisbt/wrisbtindex.py +++ b/rainbowadn/wrisbt/wrisbtindex.py @@ -33,9 +33,13 @@ class WrisbtIndex(RecursiveMentionable): async def str(self, tab: int) -> str: assert isinstance(tab, int) + total_str, delta_str = await gather( + hash_point_format(self.total, tab), + hash_point_format(self.delta, tab), + ) return f'(index)' \ - f'{tabulate(tab)}{await hash_point_format(self.total, tab)}' \ - f'{tabulate(tab)}{await hash_point_format(self.delta, tab)}' + f'{tabulate(tab)}{total_str}' \ + f'{tabulate(tab)}{delta_str}' class WrisbtIndexFactory(RainbowFactory[WrisbtIndex]): diff --git a/rainbowadn/wrisbt/wrisbtprotocol.py b/rainbowadn/wrisbt/wrisbtprotocol.py index d1cd5e5..28046c8 100644 --- a/rainbowadn/wrisbt/wrisbtprotocol.py +++ b/rainbowadn/wrisbt/wrisbtprotocol.py @@ -41,10 +41,15 @@ class WrisbtProtocol(MetaReductionStateProtocol[TargetType, WrisbtIndex]): total: WrisbtRoot = await index.total.resolve() assert isinstance(total, WrisbtRoot) + total_index, delta_index = await gather( + total.index(header, empty), + empty.index(header, total), + ) + return HashPoint.of( WrisbtIndex( - HashPoint.of(await total.index(header, empty)), - HashPoint.of(await empty.index(header, total)), + HashPoint.of(total_index), + HashPoint.of(delta_index), index.keymin ) ) diff --git a/rainbowadn/wrisbt/wrisbtroot.py b/rainbowadn/wrisbt/wrisbtroot.py index 7f6de43..156c2d9 100644 --- a/rainbowadn/wrisbt/wrisbtroot.py +++ b/rainbowadn/wrisbt/wrisbtroot.py @@ -86,7 +86,11 @@ class WrisbtRoot(RecursiveMentionable): assert isinstance(exclude, WrisbtRoot) key: bytes = target.point assert isinstance(key, bytes) - if await exclude.contains(key) or await self.contains(key): + exclude_contains, self_contains = await gather( + exclude.contains(key), + self.contains(key), + ) + if exclude_contains or self_contains: return self tree: WrisbtRoot = self value: Mentionable = await target.resolve()