From eebf439a30996fd8af4c5eb1c8dc0f6738c964e9 Mon Sep 17 00:00:00 2001 From: David Rusu Date: Tue, 6 Feb 2024 19:31:34 +0400 Subject: [PATCH] feat(leader_coin): add nonce and coin.evolve() api --- cryptarchia/cryptarchia.py | 32 ++++++++++++++++++++++--- cryptarchia/test_fork_choice.py | 2 +- cryptarchia/test_leader.py | 2 +- cryptarchia/test_ledger_state_update.py | 14 +++++------ 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/cryptarchia/cryptarchia.py b/cryptarchia/cryptarchia.py index 2df0958..8a0a87d 100644 --- a/cryptarchia/cryptarchia.py +++ b/cryptarchia/cryptarchia.py @@ -79,8 +79,24 @@ class Slot: @dataclass class Coin: - pk: int + sk: int value: int + nonce: bytes = bytes(32) + + @property + def pk(self): + return self.sk + + def evolve(self) -> "Coin": + sk_bytes = int.to_bytes(self.sk, length=32, byteorder="little") + + h = blake2b(digest_size=32) + h.update(b"coin-evolve") + h.update(sk_bytes) + h.update(self.nonce) + evolved_nonce = h.digest() + + return Coin(nonce=evolved_nonce, sk=self.sk, value=self.value) def commitment(self) -> Id: # TODO: mocked until CL is understood @@ -88,6 +104,8 @@ class Coin: value_bytes = int.to_bytes(self.value, length=32, byteorder="little") h = sha256() + h.update(b"coin-commitment") + h.update(self.nonce) h.update(pk_bytes) h.update(value_bytes) return h.digest() @@ -98,9 +116,10 @@ class Coin: value_bytes = int.to_bytes(self.value, length=32, byteorder="little") h = sha256() + h.update(b"coin-nullifier") + h.update(self.nonce) h.update(pk_bytes) h.update(value_bytes) - h.update(b"\x00") # extra 0 byte to differentiate from commitment return h.digest() @@ -108,10 +127,17 @@ class Coin: class MockLeaderProof: commitment: Id nullifier: Id + evolved_commitment: Id @staticmethod def from_coin(coin: Coin): - return MockLeaderProof(commitment=coin.commitment(), nullifier=coin.nullifier()) + evolved_coin = coin.evolve() + + return MockLeaderProof( + commitment=coin.commitment(), + nullifier=coin.nullifier(), + evolved_commitment=evolved_coin.commitment(), + ) def verify(self, slot): # TODO: verification not implemented diff --git a/cryptarchia/test_fork_choice.py b/cryptarchia/test_fork_choice.py index 87dc379..93e5571 100644 --- a/cryptarchia/test_fork_choice.py +++ b/cryptarchia/test_fork_choice.py @@ -23,7 +23,7 @@ def make_block(parent_id: Id, slot: Slot, content: bytes) -> BlockHeader: content_size=1, slot=slot, content_id=content_id, - leader_proof=MockLeaderProof.from_coin(Coin(pk=0, value=10)), + leader_proof=MockLeaderProof.from_coin(Coin(sk=0, value=10)), ) diff --git a/cryptarchia/test_leader.py b/cryptarchia/test_leader.py index ffb5554..43de3ac 100644 --- a/cryptarchia/test_leader.py +++ b/cryptarchia/test_leader.py @@ -23,7 +23,7 @@ class TestLeader(TestCase): epoch_period_nonce_stabilization=3, time=TimeConfig(slot_duration=1, chain_start_time=0), ) - l = Leader(config=config, coin=Coin(pk=0, value=10)) + l = Leader(config=config, coin=Coin(sk=0, value=10)) # We'll use the Margin of Error equation to decide how many samples we need. # https://en.wikipedia.org/wiki/Margin_of_error diff --git a/cryptarchia/test_ledger_state_update.py b/cryptarchia/test_ledger_state_update.py index 6c8ec7e..50e0539 100644 --- a/cryptarchia/test_ledger_state_update.py +++ b/cryptarchia/test_ledger_state_update.py @@ -50,7 +50,7 @@ def config() -> Config: class TestLedgerStateUpdate(TestCase): def test_ledger_state_prevents_coin_reuse(self): - leader_coin = Coin(pk=0, value=100) + leader_coin = Coin(sk=0, value=100) genesis = mk_genesis_state([leader_coin]) follower = Follower(genesis, config()) @@ -76,9 +76,9 @@ class TestLedgerStateUpdate(TestCase): assert follower.local_chain.tip() == block def test_ledger_state_is_properly_updated_on_reorg(self): - coin_1 = Coin(pk=0, value=100) - coin_2 = Coin(pk=1, value=100) - coin_3 = Coin(pk=2, value=100) + coin_1 = Coin(sk=0, value=100) + coin_2 = Coin(sk=1, value=100) + coin_3 = Coin(sk=2, value=100) genesis = mk_genesis_state([coin_1, coin_2, coin_3]) @@ -114,7 +114,7 @@ class TestLedgerStateUpdate(TestCase): assert follower.ledger_state[block_3.id()].verify_unspent(coin_1.nullifier()) def test_epoch_transition(self): - leader_coins = [Coin(pk=i, value=100) for i in range(4)] + leader_coins = [Coin(sk=i, value=100) for i in range(4)] genesis = mk_genesis_state(leader_coins) # An epoch will be 10 slots long, with stake distribution snapshot taken at the start of the epoch @@ -145,12 +145,12 @@ class TestLedgerStateUpdate(TestCase): # To ensure this is the case, we add a new coin just to the state associated with that slot, # so that the new block can be accepted only if that is the snapshot used # first, verify that if we don't change the state, the block is not accepted - block_4 = mk_block(slot=20, parent=block_3.id(), coin=Coin(pk=4, value=100)) + block_4 = mk_block(slot=20, parent=block_3.id(), coin=Coin(sk=4, value=100)) follower.on_block(block_4) assert follower.tip() == block_3 # then we add the coin to the state associated with slot 9 follower.ledger_state[block_2.id()].commitments.add( - Coin(pk=4, value=100).commitment() + Coin(sk=4, value=100).commitment() ) follower.on_block(block_4) assert follower.tip() == block_4