add missing asserts + trace/plot + optimize avl (+ remove fsplit) + more instrumentation

This commit is contained in:
AF 2022-06-23 20:37:31 +03:00
parent 001c8e1930
commit d86609d8b3
35 changed files with 382 additions and 114 deletions

1
.gitignore vendored
View File

@ -211,3 +211,4 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
/trace/

View File

@ -3,6 +3,7 @@
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
<excludeFolder url="file://$MODULE_DIR$/trace" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

97
plot.py
View File

@ -1,88 +1,31 @@
import asyncio
import random
import json
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 plottable(log: list[tuple[float, int]]):
if log:
return np.array(log).transpose()
else:
return np.array([[], []])
def plot(instrumentation: Concurrency):
plt.plot(*np.array(instrumentation.logs).transpose())
def plot():
plt.rcParams['figure.figsize'] = [16, 9]
plt.style.use("dark_background")
plt.subplots_adjust(left=0.03, right=0.99, top=0.99, bottom=0.05)
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)
with open('trace/latest.json') as file:
jsonified = json.load(file)
if (log := jsonified.get('DelayedResolver:sleep:concurrency')) is not None:
plt.plot(*plottable(log))
if (log := jsonified.get('ActiveBinaryTree:add:entry')) is not None:
plt.scatter(*plottable(log), c='tomato', zorder=100, s=.5)
if (log := jsonified.get('ActiveBinaryTree:add:exit')) is not None:
plt.scatter(*plottable(log), c='gold', zorder=99, s=.5)
plt.show()
asyncio.run(main())
if __name__ == '__main__':
plot()

View File

@ -42,6 +42,9 @@ class Block(RecursiveMentionable, Generic[HeaderType, StateType]):
hash_point_format(self.header, tab + 1),
hash_point_format(self.state, tab + 1),
)
assert isinstance(previous_str, str)
assert isinstance(header_str, str)
assert isinstance(state_str, str)
return f'{previous_str}' \
f'{tabulate(tab)}(' \
f'{tabulate(tab + 1)}block' \

View File

@ -125,6 +125,7 @@ class BlockChain(
previous: ChainCollectionInterface[
Block[HeaderType, StateType], HeaderType, ActualStateType
] = await self.previous()
assert isinstance(previous, ChainCollectionInterface)
assert_trues(
await gather(
self.verify_link(previous.reference),

View File

@ -54,6 +54,9 @@ class ActiveStageStateProtocol(
]
]
) -> bool:
assert isinstance(previous, NullableReference)
assert isinstance(header, HashPoint)
assert isinstance(state, HashPoint)
return await self.stage_state_protocol().verify(
previous,
header,

View File

@ -106,6 +106,8 @@ class StageStage(
self.previous.str(tab),
hash_point_format(self.stage, tab)
)
assert isinstance(previous_str, str)
assert isinstance(stage_str, str)
return f'{previous_str}' \
f'{tabulate(tab)}{stage_str}'
@ -195,6 +197,8 @@ class StateStage(
hash_point_format(self.previous, tab),
hash_point_format(self.state, tab),
)
assert isinstance(previous_str, str)
assert isinstance(state_str, str)
return f'{previous_str}' \
f'{tabulate(tab)}{state_str}'

View File

@ -31,6 +31,8 @@ class KeyMetadata(Keyed[ActiveKeyType], Generic[ActiveKeyType, MetaDataType]):
hash_point_format(self.key, tab),
hash_point_format(self.metadata, tab)
)
assert isinstance(key_str, str)
assert isinstance(metadata_str, str)
return f'{key_str}' \
f'{tabulate(tab)}{metadata_str}'

View File

@ -35,6 +35,8 @@ class Stack(RecursiveMentionable, Generic[ElementType]):
self.previous.str(tab),
hash_point_format(self.element, tab),
)
assert isinstance(previous_str, str)
assert isinstance(element_str, str)
return f'{previous_str}' \
f'{tabulate(tab)}{element_str}'
@ -76,6 +78,7 @@ class Stack(RecursiveMentionable, Generic[ElementType]):
pass
else:
stack: Stack[ElementType] = await reference.resolve()
assert isinstance(stack, Stack)
yield stack.element
async for element in cls.iter(stack.previous):
yield element
@ -85,6 +88,7 @@ class Stack(RecursiveMentionable, Generic[ElementType]):
cls,
reference: NullableReference['Stack[ElementType]']
) -> list[ElementType]:
assert isinstance(reference, NullableReference)
return list(
await gather(
*[element.resolve() async for element in cls.iter(reference)]

View File

@ -33,6 +33,8 @@ class Pair(
hash_point_format(self.element0, tab),
hash_point_format(self.element1, tab),
)
assert isinstance(e0_str, str)
assert isinstance(e1_str, str)
return f'(pair)' \
f'{tabulate(tab)}{e0_str}' \
f'{tabulate(tab)}{e1_str}'

View File

@ -15,9 +15,11 @@ class BinaryAction(Generic[ActiveKeyType, MetaDataType, TreeType, ActionType]):
self,
protocolized: BinaryProtocolized[ActiveKeyType, MetaDataType, TreeType],
) -> ActionType:
assert isinstance(protocolized, BinaryProtocolized)
if (split := await ProtocolizedBinarySplit.split_of(protocolized)) is None:
return await self.on_null(protocolized)
else:
assert isinstance(split, ProtocolizedBinarySplit)
return await self.on_split(split)
async def on_null(

View File

@ -26,6 +26,8 @@ class AddAction(
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType],
equal: Equal
) -> TreeType:
assert isinstance(case, ProtocolizedBinarySplit)
assert isinstance(equal, Equal)
if isinstance(equal, Replace):
return await case.protocol.tree(case.split.treel, case.split.treer, self.key)
else:
@ -35,6 +37,7 @@ class AddAction(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(case, ProtocolizedBinarySplit)
return await case.protocol.tree(
await self.on(case.protocolizedl()),
case.split.treer,
@ -45,6 +48,7 @@ class AddAction(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(case, ProtocolizedBinarySplit)
return await case.protocol.tree(
case.split.treel,
await self.on(case.protocolizedr()),
@ -75,18 +79,18 @@ class MergeAction(
self,
protocolized: BinaryProtocolized[ActiveKeyType, MetaDataType, TreeType],
) -> TreeType:
assert isinstance(protocolized, BinaryProtocolized)
return self.treer
async def on_split(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
protocol = case.protocol
split = case.split
return await protocol.tree(
split.treel,
assert isinstance(case, ProtocolizedBinarySplit)
return await case.protocol.tree(
case.split.treel,
await MergeAction(self.treer).on(case.protocolizedr()),
split.key
case.split.key
)
@ -104,12 +108,15 @@ class RemoveAction(
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType],
equal: Equal
) -> TreeType:
assert isinstance(case, ProtocolizedBinarySplit)
assert isinstance(equal, Equal)
return await MergeAction(case.split.treer).on(case.protocolizedl())
async def on_left(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(case, ProtocolizedBinarySplit)
return await case.protocol.tree(
await self.on(case.protocolizedl()),
case.split.treer,
@ -120,6 +127,7 @@ class RemoveAction(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(case, ProtocolizedBinarySplit)
return await case.protocol.tree(
case.split.treel,
await self.on(case.protocolizedr()),
@ -130,6 +138,7 @@ class RemoveAction(
self,
protocolized: BinaryProtocolized[ActiveKeyType, MetaDataType, TreeType],
) -> TreeType:
assert isinstance(protocolized, BinaryProtocolized)
return protocolized.tree
@ -147,22 +156,27 @@ class ContainsAction(
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType],
equal: Equal
) -> bool:
assert isinstance(case, ProtocolizedBinarySplit)
assert isinstance(equal, Equal)
return True
async def on_left(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> bool:
assert isinstance(case, ProtocolizedBinarySplit)
return await self.on(case.protocolizedl())
async def on_right(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> bool:
assert isinstance(case, ProtocolizedBinarySplit)
return await self.on(case.protocolizedr())
async def on_null(
self,
protocolized: BinaryProtocolized[ActiveKeyType, MetaDataType, TreeType],
) -> bool:
assert isinstance(protocolized, BinaryProtocolized)
return False

View File

@ -17,6 +17,7 @@ class Symmetric(
self,
protocol: BinaryCreation[ActiveKeyType, MetaDataType, TreeType],
):
assert isinstance(protocol, BinaryCreation)
self.protocol = protocol
def inner(
@ -43,12 +44,14 @@ class Symmetric(
self,
split: BinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> BinaryProtocolized[ActiveKeyType, MetaDataType, TreeType]:
assert isinstance(split, BinarySplit)
return BinaryProtocolized(self.protocol, self.inner(split))
def protocolizedo(
self,
split: BinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> BinaryProtocolized[ActiveKeyType, MetaDataType, TreeType]:
assert isinstance(split, BinarySplit)
return BinaryProtocolized(self.protocol, self.outer(split))
@ -57,12 +60,14 @@ class InnerOuter(Symmetric):
self,
split: BinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(split, BinarySplit)
return split.treel
def outer(
self,
split: BinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(split, BinarySplit)
return split.treer
async def tree(
@ -71,6 +76,7 @@ class InnerOuter(Symmetric):
outer: TreeType,
key: HashPoint[ActiveKeyType]
) -> TreeType:
assert isinstance(key, HashPoint)
return await self.protocol.tree(inner, outer, key)
@ -79,12 +85,14 @@ class OuterInner(Symmetric):
self,
split: BinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(split, BinarySplit)
return split.treer
def outer(
self,
split: BinarySplit[ActiveKeyType, MetaDataType, TreeType]
) -> TreeType:
assert isinstance(split, BinarySplit)
return split.treel
async def tree(
@ -93,4 +101,5 @@ class OuterInner(Symmetric):
outer: TreeType,
key: HashPoint[ActiveKeyType]
) -> TreeType:
assert isinstance(key, HashPoint)
return await self.protocol.tree(outer, inner, key)

View File

@ -1,4 +1,4 @@
from typing import Generic, TypeVar
from typing import Generic, Optional, TypeVar
from rainbowadn.atomic import *
from rainbowadn.core import *
@ -22,11 +22,14 @@ class AVL(BinaryBalancing[ActiveKeyType, Integer, TreeType]):
key: HashPoint[ActiveKeyType],
creation: BinaryCreation[ActiveKeyType, Integer, TreeType]
) -> HashPoint[Integer]:
assert isinstance(key, HashPoint)
assert isinstance(creation, BinaryCreation)
height_l, height_r = await gather(
self.height(BinaryProtocolized(creation, treel)),
self.height(BinaryProtocolized(creation, treer)),
)
assert isinstance(height_l, int)
assert isinstance(height_r, int)
return HashPoint.of(
Integer(
1
@ -43,12 +46,22 @@ class AVL(BinaryBalancing[ActiveKeyType, Integer, TreeType]):
cls,
protocolized: BinaryProtocolized[ActiveKeyType, Integer, TreeType],
) -> int:
assert isinstance(protocolized, BinaryProtocolized)
return await HeightAction().on(protocolized)
@classmethod
async def full_height(
cls,
protocolized: BinaryProtocolized[ActiveKeyType, Integer, TreeType],
) -> tuple[int, Optional[BinarySplit[ActiveKeyType, Integer, TreeType]]]:
assert isinstance(protocolized, BinaryProtocolized)
return await FullHeightAction().on(protocolized)
async def balance(
self,
protocolized: BinaryProtocolized[ActiveKeyType, Integer, TreeType],
) -> TreeType:
assert isinstance(protocolized, BinaryProtocolized)
return await BalanceAction().on(protocolized)
@ -60,16 +73,45 @@ class HeightAction(
self,
protocolized: BinaryProtocolized[ActiveKeyType, Integer, TreeType],
) -> int:
assert isinstance(protocolized, BinaryProtocolized)
return 0
async def on_split(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, Integer, TreeType]
) -> int:
assert isinstance(case, ProtocolizedBinarySplit)
metadata: Integer = await case.split.metadata.resolve()
assert isinstance(metadata, Integer)
return metadata.integer
class FullHeightAction(
BinaryAction[
ActiveKeyType,
Integer,
TreeType,
tuple[int, Optional[BinarySplit[ActiveKeyType, Integer, TreeType]]]
],
Generic[ActiveKeyType, TreeType]
):
async def on_null(
self,
protocolized: BinaryProtocolized[ActiveKeyType, Integer, TreeType],
) -> tuple[int, Optional[BinarySplit[ActiveKeyType, Integer, TreeType]]]:
assert isinstance(protocolized, BinaryProtocolized)
return 0, None
async def on_split(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, Integer, TreeType]
) -> tuple[int, Optional[BinarySplit[ActiveKeyType, Integer, TreeType]]]:
assert isinstance(case, ProtocolizedBinarySplit)
metadata: Integer = await case.split.metadata.resolve()
assert isinstance(metadata, Integer)
return metadata.integer, case.split
class BalanceAction(
BinaryAction[ActiveKeyType, Integer, TreeType, TreeType],
Generic[ActiveKeyType, TreeType]
@ -78,22 +120,30 @@ class BalanceAction(
self,
protocolized: BinaryProtocolized[ActiveKeyType, Integer, TreeType],
) -> TreeType:
assert isinstance(protocolized, BinaryProtocolized)
return protocolized.tree
async def on_split(
self,
case: ProtocolizedBinarySplit[ActiveKeyType, Integer, TreeType]
) -> TreeType:
height_l, height_r = await gather(
AVL.height(case.protocolizedl()),
AVL.height(case.protocolizedr()),
assert isinstance(case, ProtocolizedBinarySplit)
(height_l, splitl), (height_r, splitr) = await gather(
AVL.full_height(case.protocolizedl()),
AVL.full_height(case.protocolizedr()),
)
assert isinstance(height_l, int)
assert isinstance(height_r, int)
delta = height_l - height_r
assert isinstance(delta, int)
if delta < -1:
return await self.on_symmetric(InnerOuter(case.protocol), case.split)
splitr: BinarySplit[ActiveKeyType, Integer, TreeType]
assert isinstance(splitr, BinarySplit)
return await self.on_symmetric(InnerOuter(case.protocol), case.split.treel, case.split.key, splitr)
elif delta > 1:
return await self.on_symmetric(OuterInner(case.protocol), case.split)
splitl: BinarySplit[ActiveKeyType, Integer, TreeType]
assert isinstance(splitl, BinarySplit)
return await self.on_symmetric(OuterInner(case.protocol), case.split.treer, case.split.key, splitl)
else:
return case.tree
@ -101,19 +151,24 @@ class BalanceAction(
async def on_symmetric(
cls,
symmetry: Symmetric[ActiveKeyType, Integer, TreeType],
split: BinarySplit[ActiveKeyType, Integer, TreeType]
treei: TreeType,
key: HashPoint[ActiveKeyType],
splito: BinarySplit[ActiveKeyType, Integer, TreeType],
) -> TreeType:
assert isinstance(symmetry, Symmetric)
assert isinstance(split, BinarySplit)
splito = await symmetry.protocol.fsplit(symmetry.outer(split))
height_oi, height_oo = await gather(
AVL.height(symmetry.protocolizedi(splito)),
assert isinstance(key, HashPoint)
assert isinstance(splito, BinarySplit)
(height_oi, splitoi), height_oo = await gather(
AVL.full_height(symmetry.protocolizedi(splito)),
AVL.height(symmetry.protocolizedo(splito)),
)
assert isinstance(height_oi, int)
assert isinstance(height_oo, int)
if height_oi > height_oo:
splitoi = await symmetry.protocol.fsplit(symmetry.inner(splito))
splitoi: BinarySplit[ActiveKeyType, Integer, TreeType]
assert isinstance(splitoi, BinarySplit)
inner, outer = await gather(
symmetry.tree(symmetry.inner(split), symmetry.inner(splitoi), split.key),
symmetry.tree(treei, symmetry.inner(splitoi), key),
symmetry.tree(symmetry.outer(splitoi), symmetry.outer(splito), splito.key),
)
return await symmetry.tree(
@ -123,7 +178,7 @@ class BalanceAction(
)
else:
return await symmetry.tree(
await symmetry.tree(symmetry.inner(split), symmetry.inner(splito), split.key),
await symmetry.tree(treei, symmetry.inner(splito), key),
symmetry.outer(splito),
splito.key
)

View File

@ -42,6 +42,9 @@ class BinaryTree(RecursiveMentionable, Generic[TreeKeyType]):
hash_point_format(self.key, tab),
self.treer.str(tab),
)
assert isinstance(treel_str, str)
assert isinstance(key_str, str)
assert isinstance(treer_str, str)
return f'{treel_str}' \
f'{tabulate(tab)}{key_str}' \
f'{tabulate(tab)}{treer_str}'

View File

@ -22,12 +22,5 @@ class BinaryCreation(Generic[ActiveKeyType, MetaDataType, TreeType]):
"""result of this method is supposed to be used right after the call, therefore all values are resolved"""
raise NotImplementedError
async def fsplit(self, tree: TreeType) -> BinarySplit[
ActiveKeyType, MetaDataType, TreeType
]:
split: Optional[BinarySplit[ActiveKeyType, MetaDataType, TreeType]] = await self.split(tree)
assert split is not None
return split
async def tree(self, treel: TreeType, treer: TreeType, key: HashPoint[ActiveKeyType]) -> TreeType:
raise NotImplementedError

View File

@ -42,6 +42,7 @@ class ProtocolizedBinarySplit(
) -> Optional[
'ProtocolizedBinarySplit[ActiveKeyType, MetaDataType, TreeType]'
]:
assert isinstance(protocolized, BinaryProtocolized)
if (split := await protocolized.split()) is None:
return None
else:

View File

@ -3,6 +3,7 @@ from typing import TypeVar
from .hashpoint import HashPoint
from .hashresolver import HashResolver
from .mentionable import Mentionable
from .resolvermetaorigin import ResolverMetaOrigin
__all__ = ('ExtendableResolver',)
@ -19,4 +20,5 @@ class ExtendableResolver(HashResolver, abc.ABC):
return ResolverMetaOrigin(await self.extend(hash_point)).hash_point(hash_point.factory, hash_point.point)
async def migrate_resolved(self, mentioned: Mentioned) -> Mentioned:
assert isinstance(mentioned, Mentionable)
return await (await self.migrate(HashPoint.of(mentioned))).resolve()

View File

@ -9,6 +9,7 @@ async def hash_point_format(hash_point: HashPoint, tab: int) -> str:
assert isinstance(hash_point, HashPoint)
assert isinstance(tab, int)
value: Mentionable = await hash_point.resolve()
assert isinstance(value, Mentionable)
if isinstance(value, RecursiveMentionable):
return await value.str(tab)
else:

View File

@ -9,6 +9,7 @@ Mentioned = TypeVar('Mentioned')
class Origin(Generic[Mentioned]):
def __init__(self, factory: RainbowFactory[Mentioned]):
assert isinstance(factory, RainbowFactory)
self.factory = factory
async def resolve(self) -> Mentioned:

View File

@ -25,6 +25,7 @@ class StaticMentionable(Mentionable, abc.ABC):
class StaticFactory(RainbowFactory[StaticMentioned], Generic[StaticMentioned]):
def __init__(self, cls: Type[StaticMentioned]):
assert issubclass(cls, StaticMentionable)
self.cls: Type[StaticMentioned] = cls
def from_bytes(self, source: bytes, resolver: HashResolver) -> StaticMentioned:

View File

@ -26,6 +26,7 @@ class Encrypted(RecursiveMentionable, Generic[EncryptedType]):
assert isinstance(decrypted, Mentionable)
self.factory: RainbowFactory[EncryptedType] = decrypted.__factory__()
assert isinstance(self.factory, RainbowFactory)
self.key = key
self.resolution = resolution
@ -38,6 +39,7 @@ class Encrypted(RecursiveMentionable, Generic[EncryptedType]):
@classmethod
async def encrypt(cls, decrypted: EncryptedType, key: bytes) -> 'Encrypted[EncryptedType]':
assert isinstance(decrypted, Mentionable)
assert isinstance(key, bytes)
hashpoints = tuple(decrypted.points()) if isinstance(decrypted, RecursiveMentionable) else ()
resolution = tuple(
@ -79,6 +81,10 @@ class Encrypted(RecursiveMentionable, Generic[EncryptedType]):
hashpoints: tuple[HashPoint, ...],
decrypted: EncryptedType
) -> 'Encrypted[EncryptedType]':
assert isinstance(key, bytes)
assert isinstance(resolution, tuple)
assert isinstance(hashpoints, tuple)
assert isinstance(decrypted, Mentionable)
mapping: dict[bytes, HashPoint[Encrypted]] = {
hash_point.point: encrypted for hash_point, encrypted in zip(hashpoints, resolution)
}
@ -122,6 +128,7 @@ class EncryptedFactory(RainbowFactory[Encrypted[EncryptedType]], Generic[Encrypt
plain[8 + resolution_size * HashPoint.HASH_LENGTH:],
resolver,
)
assert isinstance(decrypted, Mentionable)
hashpoints = tuple(decrypted.points()) if isinstance(decrypted, RecursiveMentionable) else ()
assert_eq(len(hashpoints), resolution_size)
resolution: tuple[HashPoint[Encrypted], ...] = tuple(
@ -152,6 +159,7 @@ class EncryptedResolver(HashResolver):
async def resolve(self, point: bytes) -> tuple[bytes, 'HashResolver']:
assert isinstance(point, bytes)
encrypted: Encrypted = await self.mapping[point].resolve()
assert isinstance(encrypted, Encrypted)
return HashPoint.bytes_of_mentioned(encrypted.decrypted), EncryptedResolver(encrypted.mapping, self.key)
@ -167,7 +175,9 @@ class ShortcutOrigin(Origin[Encrypted[EncryptedType]], Generic[EncryptedType]):
async def resolve(self) -> Encrypted[EncryptedType]:
source: Encrypted = await self.hashpoint.resolve()
assert isinstance(source, Encrypted)
encrypted: Encrypted[EncryptedType] = self.factory.from_bytes(bytes(source), ShortcutResolver(source))
assert isinstance(encrypted, Encrypted)
assert_eq(HashPoint.of(encrypted), self.hashpoint)
return encrypted
@ -188,6 +198,7 @@ class ShortcutResolver(HashResolver):
async def resolve(self, point: bytes) -> tuple[bytes, 'HashResolver']:
assert isinstance(point, bytes)
resolved: Encrypted = await self.mapping[point].resolve()
assert isinstance(resolved, Encrypted)
return (
HashPoint.bytes_of_mentioned(resolved),
ShortcutResolver(resolved)

View File

@ -41,9 +41,11 @@ class NullableReference(RecursiveMentionable, Generic[Referenced]):
@classmethod
def off(cls, value: Referenced) -> 'NullableReference[Referenced]':
assert isinstance(value, Mentionable)
return cls.of(HashPoint.of(value))
async def str(self, tab: int) -> str:
assert isinstance(tab, int)
if self.null():
return f'-'
else:

View File

@ -1,17 +1,21 @@
import functools
import time
from typing import Generic, TypeVar
__all__ = ('Instrumentation', 'Counter', 'Concurrency',)
from contextlib import ExitStack
from typing import Callable, Generic, TypeVar
__all__ = ('Instrumentation', 'Counter', 'Concurrency', 'EntryExit',)
IType = TypeVar('IType')
class Instrumentation(Generic[IType]):
deinstrumentation = {}
method: Callable
wrap: Callable
def __init__(self, target, methodname: str):
assert isinstance(methodname, str)
assert callable(getattr(target, methodname))
self.target = target
self.methodname = methodname
@ -21,6 +25,7 @@ class Instrumentation(Generic[IType]):
def __enter__(self: IType) -> IType:
assert not hasattr(self, 'method')
self.method = getattr(self.target, self.methodname)
assert callable(self.method)
@functools.wraps(self.method)
def wrap(*args, **kwargs):
@ -45,9 +50,13 @@ class Instrumentation(Generic[IType]):
self.schedule_deinstrumentation()
self.deinstrument()
def enter(self: IType, stack: ExitStack) -> IType:
return stack.enter_context(self)
class Counter(Instrumentation):
def __init__(self, target, methodname: str):
assert isinstance(methodname, str)
super().__init__(target, methodname)
self.counter = 0
@ -60,19 +69,39 @@ class Concurrency(Instrumentation):
start = time.time()
def __init__(self, target, methodname: str):
assert isinstance(methodname, str)
super().__init__(target, methodname)
self.concurrency = 0
self.logs: list[tuple[float, int]] = []
self.log: list[tuple[float, int]] = []
def time(self) -> float:
return time.time() - self.start
def point(self) -> tuple[float, int]:
return self.time(), self.concurrency
async def instrument(self, method, *args, **kwargs):
self.logs.append((self.time(), self.concurrency))
self.log.append(self.point())
self.concurrency += 1
self.logs.append((self.time(), self.concurrency))
self.log.append(self.point())
result = await method(*args, **kwargs)
self.logs.append((self.time(), self.concurrency))
self.log.append(self.point())
self.concurrency -= 1
self.logs.append((self.time(), self.concurrency))
self.log.append(self.point())
return result
class EntryExit(Instrumentation):
def __init__(self, target, methodname: str, point: Callable[[], tuple[float, int]]):
assert callable(point)
assert isinstance(methodname, str)
super().__init__(target, methodname)
self.point = point
self.entry_log: list[tuple[float, int]] = []
self.exit_log: list[tuple[float, int]] = []
async def instrument(self, method, *args, **kwargs):
self.entry_log.append(self.point())
result = await method(*args, **kwargs)
self.exit_log.append(self.point())
return result

View File

@ -0,0 +1,4 @@
from .cachingresolver import CachingResolver
from .delayedresolver import DelayedResolver
from .dictresolver import DictResolver
from .failresolver import FailResolver

View File

@ -0,0 +1,30 @@
from typing import TypeVar
from rainbowadn.core import *
__all__ = ('CachingResolver',)
Mentioned = TypeVar('Mentioned')
class CachingResolver(ExtendableResolver):
def __init__(self, resolver: HashResolver):
assert isinstance(resolver, HashResolver)
self.resolver = resolver
self.cache: dict[bytes, tuple[bytes, CachingResolver]] = {}
async def resolve(self, point: bytes) -> tuple[bytes, HashResolver]:
assert isinstance(point, bytes)
if point not in self.cache:
resolved, resolver = await self.resolver.resolve(point)
assert isinstance(resolved, bytes)
assert isinstance(resolver, HashResolver)
self.cache[point] = resolved, CachingResolver(resolver)
return self.cache[point]
async def extend(self, hash_point: HashPoint[Mentioned]) -> ExtendableResolver:
assert isinstance(hash_point, HashPoint)
if isinstance(self.resolver, ExtendableResolver):
return CachingResolver(await self.resolver.extend(hash_point))
else:
raise TypeError

View File

@ -10,6 +10,8 @@ Mentioned = TypeVar('Mentioned')
class DelayedResolver(ExtendableResolver):
def __init__(self, resolver: HashResolver, delay: Callable[[], float]):
assert isinstance(resolver, HashResolver)
assert callable(delay)
self.resolver = resolver
self.delay = delay
@ -20,9 +22,12 @@ class DelayedResolver(ExtendableResolver):
assert isinstance(point, bytes)
await self.sleep()
resolved, resolver = await self.resolver.resolve(point)
assert isinstance(resolved, bytes)
assert isinstance(resolver, HashResolver)
return resolved, DelayedResolver(resolver, self.delay)
async def extend(self, hash_point: HashPoint[Mentioned]) -> ExtendableResolver:
assert isinstance(hash_point, HashPoint)
if isinstance(self.resolver, ExtendableResolver):
return DelayedResolver(await self.resolver.extend(hash_point), self.delay)
else:

View File

@ -30,5 +30,6 @@ class DictResolver(ExtendableResolver):
)
async def extend(self, hash_point: HashPoint[Mentioned]) -> 'ExtendableResolver':
assert isinstance(hash_point, HashPoint)
await self.save(hash_point)
return self

View File

@ -16,9 +16,8 @@ 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 *
from .resolvers import *
class TestAll(unittest.IsolatedAsyncioTestCase):

View File

@ -20,6 +20,7 @@ class ValidReference(RecursiveMentionable, Generic[Referenced]):
async def resolve(self) -> Referenced:
referenced: Referenced = await self.reference.resolve()
assert isinstance(referenced, Mentionable)
assert self.reference.point < (await self.protocol.threshold(referenced))
return referenced

View File

@ -104,8 +104,11 @@ class BankState(RecursiveMentionable, StaticMentionable):
self,
transaction: Transaction
) -> 'BankState':
assert isinstance(transaction, Transaction)
miner = self.miner_nullable()
assert isinstance(miner, Nullable)
minted: ActiveBinaryTree[Coin, Integer] = self.minted_tree()
assert isinstance(minted, ActiveBinaryTree)
async for coin, miner in transaction.iter_coins(self.mint(), miner):
minted_contains, minted = await gather(
minted.contains(HashPoint.of(coin)),
@ -119,11 +122,13 @@ class BankState(RecursiveMentionable, StaticMentionable):
self,
transaction: Transaction
) -> 'BankState':
assert isinstance(transaction, Transaction)
verified, bank_state = await gather(
self.verify(transaction),
self._mint_coins(transaction),
)
assert_true(verified)
assert isinstance(bank_state, BankState)
return bank_state
def miner_nullable(self) -> Nullable[HashPoint[Subject]]:
@ -161,6 +166,10 @@ class BankState(RecursiveMentionable, StaticMentionable):
self.used.str(tab + 1),
hash_point_format(self.length, tab + 1),
)
assert isinstance(miner_str, str)
assert isinstance(minted_str, str)
assert isinstance(used_str, str)
assert isinstance(length_str, str)
return f'(' \
f'{tabulate(tab + 1)}bank' \
f'{tabulate(tab + 1)}(miner)' \

View File

@ -53,6 +53,8 @@ class CoinData(RecursiveMentionable, StaticMentionable):
hash_point_format(self.owner, tab),
hash_point_format(self.value, tab),
)
assert isinstance(owner_str, str)
assert isinstance(value_str, str)
return f'{owner_str}' \
f'{tabulate(tab)}{value_str}'
@ -98,6 +100,8 @@ class Coin(RecursiveMentionable, StaticMentionable):
hash_point_format(self.data, tab + 1),
hash_point_format(self.index, tab + 1),
)
assert isinstance(data_str, str)
assert isinstance(index_str, str)
return f'(' \
f'{tabulate(tab + 1)}coin' \
f'{tabulate(tab + 1)}{data_str}' \
@ -145,6 +149,8 @@ class TransactionData(RecursiveMentionable, StaticMentionable):
)
async def _signature_verify(self, coin: Coin, signature: Signature) -> bool:
assert isinstance(coin, Coin)
assert isinstance(signature, Signature)
assert_true(
signature.verify(
await coin.owner_resolved(),
@ -157,6 +163,7 @@ class TransactionData(RecursiveMentionable, StaticMentionable):
self,
signatures: NullableReference[Stack[Signature]]
) -> bool:
assert isinstance(signatures, NullableReference)
assert_trues(
await gather(
*[
@ -207,6 +214,8 @@ class TransactionData(RecursiveMentionable, StaticMentionable):
self._total_in(),
self._total_out(),
)
assert isinstance(total_in, int)
assert isinstance(total_out, int)
return total_in + mint - total_out
async def verify(
@ -230,6 +239,8 @@ class TransactionData(RecursiveMentionable, StaticMentionable):
self.in_coins.str(tab),
self.out_coins.str(tab),
)
assert isinstance(in_str, str)
assert isinstance(out_str, str)
return f'(in)' \
f'{tabulate(tab)}{in_str}' \
f'{tabulate(tab)}(out)' \
@ -276,6 +287,8 @@ class Transaction(RecursiveMentionable, StaticMentionable):
mint: int,
miner: Nullable[HashPoint[Subject]]
) -> AsyncIterable[tuple[Coin, Nullable[HashPoint[Subject]]]]:
assert isinstance(mint, int)
assert isinstance(miner, Nullable)
transaction_data: TransactionData = await self.data_resolved()
assert isinstance(transaction_data, TransactionData)
index = 0
@ -325,6 +338,8 @@ class Transaction(RecursiveMentionable, StaticMentionable):
hash_point_format(self.data, tab + 1),
self.signatures.str(tab + 1),
)
assert isinstance(data_str, str)
assert isinstance(signatures_str, str)
return f'(' \
f'{tabulate(tab + 1)}transaction' \
f'{tabulate(tab + 1)}{data_str}' \
@ -338,6 +353,9 @@ class Transaction(RecursiveMentionable, StaticMentionable):
out_coins: list[CoinData],
keys: list[nacl.signing.SigningKey],
) -> 'Transaction':
assert isinstance(in_coins, list)
assert isinstance(out_coins, list)
assert isinstance(keys, list)
transaction_data = TransactionData(
Stack.off(Coin.factory(), reversed(in_coins)),
Stack.off(CoinData.factory(), reversed(out_coins)),

View File

@ -271,12 +271,19 @@ class WeakReferenceIndexSetBTree(RecursiveMentionable):
for key_index in range(self.keys):
yield self.key_no(key_index)
else:
children: list[WeakReferenceIndexSetBTree] = list(
await gather(
*(
self.child_resolved_no(child) for child in range(self.children)
)
)
)
for index in range(self.keys * 2 + 1):
real_index, mode = divmod(index, 2)
if mode:
yield self.key_no(real_index)
else:
async for key in (await self.child_resolved_no(real_index)).iter_keys():
async for key in children[real_index].iter_keys():
yield key

106
trace.py Normal file
View File

@ -0,0 +1,106 @@
import asyncio
import json
import os
import random
import shutil
import time
from contextlib import ExitStack
from nacl.signing import SigningKey
from plot import plot
from rainbowadn.chain import *
from rainbowadn.collection.trees.binary import *
from rainbowadn.core import *
from rainbowadn.nullability import *
from rainbowadn.testing.instrumentation import *
from rainbowadn.testing.resolvers import *
from rainbowadn.v13 import *
def get_dr() -> ExtendableResolver:
dr = DictResolver()
dr = DelayedResolver(dr, lambda: random.uniform(0.200, 0.500))
# dr = CachingResolver(dr)
return dr
def target_str(target) -> str:
match target:
case type(__name__=name):
return name
case object(__class__=type(__name__=name)):
return name
def jsonify(instrumentation: Instrumentation) -> dict:
prefix = f'{target_str(instrumentation.target)}:{instrumentation.methodname}'
match instrumentation:
case Counter(counter=counter):
return {f'{prefix}:counter': counter}
case Concurrency(log=log):
return {f'{prefix}:concurrency': log}
case EntryExit(entry_log=entry_log, exit_log=exit_log):
return {
f'{prefix}:entry': entry_log,
f'{prefix}:exit': exit_log,
}
case _:
return {}
async def main():
set_gather_linear()
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 ExitStack() as stack:
sleep_cc = Concurrency(DelayedResolver, 'sleep').enter(stack)
avl_ex = EntryExit(ActiveBinaryTree, 'add', sleep_cc.point).enter(stack)
assert_true(await bank.verify())
print(Instrumentation.deinstrumentation)
fn = f'trace/{int(time.time())}-{os.urandom(2).hex()}.json'
with open(fn, 'w') as file:
json.dump(
jsonify(sleep_cc) | jsonify(avl_ex),
file
)
shutil.copy(fn, f'trace/latest.json')
plot()
if __name__ == '__main__':
asyncio.run(main())