cryptarchia: drop note evolution

This commit is contained in:
David Rusu 2025-03-21 02:47:55 +04:00
parent 17bec9b570
commit 69464eb5a5
6 changed files with 116 additions and 139 deletions

View File

@ -159,10 +159,6 @@ class Note:
def encode_pk(self) -> bytes:
return int.to_bytes(self.pk, length=32, byteorder="big")
def evolve(self) -> "Note":
evolved_nonce = Hash(b"coin-evolve", self.encode_sk(), self.nonce)
return Note(nonce=evolved_nonce, sk=self.sk, value=self.value)
def commitment(self) -> Hash:
value_bytes = int.to_bytes(self.value, length=32, byteorder="big")
return Hash(

View File

@ -26,7 +26,6 @@ class TestNode:
parent = self.follower.tip_id()
epoch_state = self.epoch_state(slot)
if leader_proof := self.leader.try_prove_slot_leader(epoch_state, slot, parent):
self.leader.note = self.leader.note.evolve()
return BlockHeader(
parent=parent,
slot=slot,
@ -89,5 +88,4 @@ def mk_chain(
block = mk_block(parent=parent, slot=s, note=note)
chain.append(block)
parent = block
note = note.evolve()
return chain, note
return chain

View File

@ -174,18 +174,18 @@ class TestForkChoice(TestCase):
genesis = mk_genesis_state([]).block
short_note, long_note = Note(sk=0, value=100), Note(sk=1, value=100)
common, long_note = mk_chain(parent=genesis, note=long_note, slots=range(50))
common = mk_chain(parent=genesis, note=long_note, slots=range(50))
long_chain_sparse_ext, long_note = mk_chain(
long_chain_sparse_ext = mk_chain(
parent=common[-1], note=long_note, slots=range(50, 100, 2)
)
short_chain_dense_ext, _ = mk_chain(
short_chain_dense_ext = mk_chain(
parent=common[-1], note=short_note, slots=range(50, 100)
)
# add more blocks to the long chain to ensure the long chain is indeed longer
long_chain_further_ext, _ = mk_chain(
long_chain_further_ext = mk_chain(
parent=long_chain_sparse_ext[-1], note=long_note, slots=range(100, 126)
)
@ -214,16 +214,16 @@ class TestForkChoice(TestCase):
def test_fork_choice_long_dense_chain(self):
# The longest chain is also the densest after the fork
short_note, long_note = Note(sk=0, value=100), Note(sk=1, value=100)
common, long_note = mk_chain(
common = mk_chain(
parent=mk_genesis_state([]).block,
note=long_note,
slots=range(1, 50),
)
long_chain_dense_ext, _ = mk_chain(
long_chain_dense_ext = mk_chain(
parent=common[-1], note=long_note, slots=range(50, 100)
)
short_chain_sparse_ext, _ = mk_chain(
short_chain_sparse_ext = mk_chain(
parent=common[-1], note=short_note, slots=range(50, 100, 2)
)
@ -246,7 +246,7 @@ class TestForkChoice(TestCase):
genesis = mk_genesis_state(notes)
follower = Follower(genesis, config)
b1, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1 = mk_block(genesis.block, 1, n_a)
follower.on_block(b1)
@ -262,8 +262,8 @@ class TestForkChoice(TestCase):
# b3
#
b2, n_a = mk_block(b1, 2, n_a), n_a.evolve()
b3, n_b = mk_block(b1, 2, n_b), n_b.evolve()
b2 = mk_block(b1, 2, n_a)
b3 = mk_block(b1, 2, n_b)
follower.on_block(b2)
follower.on_block(b3)
@ -280,7 +280,7 @@ class TestForkChoice(TestCase):
# b3 - b4 == tip
#
b4, n_b = mk_block(b3, 3, n_b), n_a.evolve()
b4 = mk_block(b3, 3, n_b)
follower.on_block(b4)
assert follower.tip_id() == b4.id()

View File

@ -32,7 +32,7 @@ class TestLedgerStateUpdate(TestCase):
assert len(follower.ledger_state) == 2
assert len(follower.forks) == 0
def test_ledger_state_prevents_note_reuse(self):
def test_ledger_state_allows_note_reuse(self):
leader_note = Note(sk=0, value=100)
genesis = mk_genesis_state([leader_note])
@ -45,16 +45,12 @@ class TestLedgerStateUpdate(TestCase):
assert len(list(iter_chain(follower.tip_id(), follower.ledger_state))) == 2
assert follower.tip() == block
# Follower should have updated their ledger state to mark the leader note as spent
assert follower.tip_state().verify_unspent(leader_note.nullifier()) == False
reuse_note_block = mk_block(slot=1, parent=block, note=leader_note)
with self.assertRaises(InvalidLeaderProof):
follower.on_block(reuse_note_block)
follower.on_block(reuse_note_block)
# Follower should *not* have accepted the block
assert len(list(iter_chain(follower.tip_id(), follower.ledger_state))) == 2
assert follower.tip() == block
# Follower should have accepted the block
assert len(list(iter_chain(follower.tip_id(), follower.ledger_state))) == 3
assert follower.tip() == reuse_note_block
def test_ledger_state_is_properly_updated_on_reorg(self):
note = [Note(sk=0, value=100), Note(sk=1, value=100), Note(sk=2, value=100)]
@ -72,7 +68,6 @@ class TestLedgerStateUpdate(TestCase):
follower.on_block(block_1)
assert follower.tip() == block_1
assert not follower.tip_state().verify_unspent(note[0].nullifier())
# 3) then sees block 2, but sticks with block_1 as the tip
@ -87,9 +82,6 @@ class TestLedgerStateUpdate(TestCase):
# the follower should have switched over to the block_2 fork
assert follower.tip() == block_3
# and the original note[0] should now be removed from the spent pool
assert follower.tip_state().verify_unspent(note[0].nullifier())
def test_fork_creation(self):
notes = [Note(sk=i, value=100) for i in range(7)]
genesis = mk_genesis_state(notes)
@ -177,15 +169,15 @@ class TestLedgerStateUpdate(TestCase):
with self.assertRaises(InvalidLeaderProof):
follower.on_block(block_4)
assert follower.tip() == block_3
# then we add the note to "spendable commitments" associated with slot 9
follower.ledger_state[block_2.id()].commitments_spend.add(
# then we add the note to "commitments" associated with slot 9
follower.ledger_state[block_2.id()].commitments.add(
Note(sk=4, value=100).commitment()
)
follower.on_block(block_4)
assert follower.tip() == block_4
assert follower.tip().slot.epoch(config).epoch == 2
def test_evolved_note_is_eligible_for_leadership(self):
def test_note_added_after_stake_freeze_is_ineligible_for_leadership(self):
note = Note(sk=0, value=100)
genesis = mk_genesis_state([note])
@ -197,16 +189,19 @@ class TestLedgerStateUpdate(TestCase):
follower.on_block(block_1)
assert follower.tip() == block_1
# note can't be reused to win following slots:
block_2_reuse = mk_block(slot=1, parent=block_1, note=note)
with self.assertRaises(InvalidLeaderProof):
follower.on_block(block_2_reuse)
assert follower.tip() == block_1
# note can be reused to win following slots:
block_2 = mk_block(slot=1, parent=block_1, note=note)
follower.on_block(block_2)
assert follower.tip() == block_2
# but the evolved note is eligible
block_2_evolve = mk_block(slot=1, parent=block_1, note=note.evolve())
follower.on_block(block_2_evolve)
assert follower.tip() == block_2_evolve
# but the a new note is ineligible
note_new = Note(sk=1, value=10)
follower.tip_state().commitments.add(note_new.commitment())
block_3_new = mk_block(slot=2, parent=block_2, note=note_new)
with self.assertRaises(InvalidLeaderProof):
follower.on_block(block_3_new)
assert follower.tip() == block_2
def test_new_notes_becoming_eligible_after_stake_distribution_stabilizes(self):
note = Note(sk=0, value=100)
@ -225,9 +220,7 @@ class TestLedgerStateUpdate(TestCase):
# mint a new note to be used for leader elections in upcoming epochs
note_new = Note(sk=1, value=10)
follower.ledger_state[block_0_0.id()].commitments_spend.add(
note_new.commitment()
)
follower.ledger_state[block_0_0.id()].commitments.add(note_new.commitment())
# the new note is not yet eligible for elections
block_0_1_attempt = mk_block(slot=1, parent=block_0_0, note=note_new)
@ -235,30 +228,20 @@ class TestLedgerStateUpdate(TestCase):
follower.on_block(block_0_1_attempt)
assert follower.tip() == block_0_0
# whereas the evolved note from genesis can be spent immediately
block_0_1 = mk_block(slot=1, parent=block_0_0, note=note.evolve())
follower.on_block(block_0_1)
assert follower.tip() == block_0_1
# ---- EPOCH 1 ----
# The newly minted note is still not eligible in the following epoch since the
# stake distribution snapshot is taken at the beginning of the previous epoch
block_1_0 = mk_block(slot=20, parent=block_0_1, note=note_new)
block_1_0_attempt = mk_block(slot=20, parent=block_0_0, note=note_new)
with self.assertRaises(InvalidLeaderProof):
follower.on_block(block_1_0)
assert follower.tip() == block_0_1
follower.on_block(block_1_0_attempt)
assert follower.tip() == block_0_0
# ---- EPOCH 2 ----
# The note is finally eligible 2 epochs after it was first minted
block_2_0 = mk_block(slot=40, parent=block_0_1, note=note_new)
block_2_0 = mk_block(slot=40, parent=block_0_0, note=note_new)
follower.on_block(block_2_0)
assert follower.tip() == block_2_0
# And now the minted note can freely use the evolved note for subsequent blocks
block_2_1 = mk_block(slot=40, parent=block_2_0, note=note_new.evolve())
follower.on_block(block_2_1)
assert follower.tip() == block_2_1

View File

@ -32,7 +32,7 @@ class TestStakeRelativization(TestCase):
assert follower.tip_state().leader_count == 1
# continuing the chain increments the leader count
b2 = mk_block(b1, slot=2, note=n_a.evolve())
b2 = mk_block(b1, slot=2, note=n_a)
follower.on_block(b2)
assert follower.tip_state().block == b2
assert follower.tip_state().leader_count == 2

View File

@ -13,10 +13,10 @@ class TestSync(TestCase):
config = mk_config([note])
genesis = mk_genesis_state([note])
peer = Follower(genesis, config)
b0, note = mk_block(genesis.block, 1, note), note.evolve()
b1, note = mk_block(b0, 2, note), note.evolve()
b2, note = mk_block(b1, 3, note), note.evolve()
b3, note = mk_block(b2, 4, note), note.evolve()
b0 = mk_block(genesis.block, 1, note)
b1 = mk_block(b0, 2, note)
b2 = mk_block(b1, 3, note)
b3 = mk_block(b2, 4, note)
for b in [b0, b1, b2, b3]:
peer.on_block(b)
self.assertEqual(peer.tip(), b3)
@ -36,10 +36,10 @@ class TestSync(TestCase):
config = mk_config([note])
genesis = mk_genesis_state([note])
peer = Follower(genesis, config)
b0, note = mk_block(genesis.block, 1, note), note.evolve()
b1, note = mk_block(b0, 2, note), note.evolve()
b2, note = mk_block(b1, 3, note), note.evolve()
b3, note = mk_block(b2, 4, note), note.evolve()
b0 = mk_block(genesis.block, 1, note)
b1 = mk_block(b0, 2, note)
b2 = mk_block(b1, 3, note)
b3 = mk_block(b2, 4, note)
for b in [b0, b1, b2, b3]:
peer.on_block(b)
self.assertEqual(peer.tip(), b3)
@ -65,12 +65,12 @@ class TestSync(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b3, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b4, n_b = mk_block(b3, 3, n_b), n_b.evolve()
b5, n_a = mk_block(b2, 4, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b1, 3, n_a)
b3 = mk_block(b0, 2, n_b)
b4 = mk_block(b3, 3, n_b)
b5 = mk_block(b2, 4, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -92,12 +92,12 @@ class TestSync(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b3, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b4, n_b = mk_block(b3, 3, n_b), n_b.evolve()
b5, n_a = mk_block(b2, 4, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b1, 3, n_a)
b3 = mk_block(b0, 2, n_b)
b4 = mk_block(b3, 3, n_b)
b5 = mk_block(b2, 4, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -125,12 +125,12 @@ class TestSync(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b3, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b4, n_b = mk_block(b3, 3, n_b), n_b.evolve()
b5, n_a = mk_block(b2, 4, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b1, 3, n_a)
b3 = mk_block(b0, 2, n_b)
b4 = mk_block(b3, 3, n_b)
b5 = mk_block(b2, 4, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -159,12 +159,12 @@ class TestSync(TestCase):
n_a, n_b = Note(sk=0, value=10), Note(sk=1, value=10)
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b3, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b4, n_b = mk_block(b3, 3, n_b), n_b.evolve()
b5, n_a = mk_block(b2, 4, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b1, 3, n_a)
b3 = mk_block(b0, 2, n_b)
b4 = mk_block(b3, 3, n_b)
b5 = mk_block(b2, 4, n_a)
peer0 = Follower(genesis, config)
for b in [b0, b1, b2, b5]:
peer0.on_block(b)
@ -204,10 +204,10 @@ class TestSync(TestCase):
config = mk_config([note])
genesis = mk_genesis_state([note])
peer = Follower(genesis, config)
b0, note = mk_block(genesis.block, 1, note), note.evolve()
b1, note = mk_block(b0, 2, note), note.evolve()
b2, note = mk_block(b1, 3, note), note.evolve()
b3, note = mk_block(b2, 4, note), note.evolve()
b0 = mk_block(genesis.block, 1, note)
b1 = mk_block(b0, 2, note)
b2 = mk_block(b1, 3, note)
b3 = mk_block(b2, 4, note)
for b in [b0, b1, b2, b3]:
peer.on_block(b)
self.assertEqual(peer.tip(), b3)
@ -215,8 +215,8 @@ class TestSync(TestCase):
# And deliberately, add invalid blocks (b4 ~ b5):
fake_note = Note(sk=1, value=10)
b4, fake_note = mk_block(b3, 5, fake_note), fake_note.evolve()
b5, fake_note = mk_block(b4, 6, fake_note), fake_note.evolve()
b4 = mk_block(b3, 5, fake_note)
b5 = mk_block(b4, 6, fake_note)
apply_invalid_block_to_ledger_state(peer, b4)
apply_invalid_block_to_ledger_state(peer, b5)
# the tip shouldn't be changed.
@ -243,12 +243,12 @@ class TestSync(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b3, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b4, n_a = mk_block(b3, 4, n_a), n_a.evolve()
b5, n_a = mk_block(b4, 5, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b0, 2, n_b)
b3 = mk_block(b1, 3, n_a)
b4 = mk_block(b3, 4, n_a)
b5 = mk_block(b4, 5, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -256,8 +256,8 @@ class TestSync(TestCase):
# And deliberately, add invalid blocks (b6 ~ b7):
fake_note = Note(sk=2, value=10)
b6, fake_note = mk_block(b2, 3, fake_note), fake_note.evolve()
b7, fake_note = mk_block(b6, 4, fake_note), fake_note.evolve()
b6 = mk_block(b2, 3, fake_note)
b7 = mk_block(b6, 4, fake_note)
apply_invalid_block_to_ledger_state(peer, b6)
apply_invalid_block_to_ledger_state(peer, b7)
# the tip shouldn't be changed.
@ -291,10 +291,10 @@ class TestSyncFromCheckpoint(TestCase):
config = mk_config([note])
genesis = mk_genesis_state([note])
peer = Follower(genesis, config)
b0, note = mk_block(genesis.block, 1, note), note.evolve()
b1, note = mk_block(b0, 2, note), note.evolve()
b2, note = mk_block(b1, 3, note), note.evolve()
b3, note = mk_block(b2, 4, note), note.evolve()
b0 = mk_block(genesis.block, 1, note)
b1 = mk_block(b0, 2, note)
b2 = mk_block(b1, 3, note)
b3 = mk_block(b2, 4, note)
for b in [b0, b1, b2, b3]:
peer.on_block(b)
self.assertEqual(peer.tip(), b3)
@ -328,12 +328,12 @@ class TestSyncFromCheckpoint(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b3, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b4, n_b = mk_block(b3, 3, n_b), n_b.evolve()
b5, n_a = mk_block(b2, 4, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b1, 3, n_a)
b3 = mk_block(b0, 2, n_b)
b4 = mk_block(b3, 3, n_b)
b5 = mk_block(b2, 4, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -366,12 +366,12 @@ class TestSyncFromCheckpoint(TestCase):
n_a, n_b = Note(sk=0, value=10), Note(sk=1, value=10)
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b3, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b4, n_b = mk_block(b3, 3, n_b), n_b.evolve()
b5, n_a = mk_block(b2, 4, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b1, 3, n_a)
b3 = mk_block(b0, 2, n_b)
b4 = mk_block(b3, 3, n_b)
b5 = mk_block(b2, 4, n_a)
peer0 = Follower(genesis, config)
for b in [b0, b1, b2, b5]:
peer0.on_block(b)
@ -411,12 +411,12 @@ class TestSyncFromCheckpoint(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b3, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b4, n_a = mk_block(b3, 4, n_a), n_a.evolve()
b5, n_a = mk_block(b4, 5, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b0, 2, n_b)
b3 = mk_block(b1, 3, n_a)
b4 = mk_block(b3, 4, n_a)
b5 = mk_block(b4, 5, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -424,8 +424,8 @@ class TestSyncFromCheckpoint(TestCase):
# And deliberately, add invalid blocks (b6 ~ b7):
fake_note = Note(sk=2, value=10)
b6, fake_note = mk_block(b2, 3, fake_note), fake_note.evolve()
b7, fake_note = mk_block(b6, 4, fake_note), fake_note.evolve()
b6 = mk_block(b2, 3, fake_note)
b7 = mk_block(b6, 4, fake_note)
apply_invalid_block_to_ledger_state(peer, b6)
apply_invalid_block_to_ledger_state(peer, b7)
# the tip shouldn't be changed.
@ -461,12 +461,12 @@ class TestSyncFromCheckpoint(TestCase):
config = mk_config([n_a, n_b])
genesis = mk_genesis_state([n_a, n_b])
peer = Follower(genesis, config)
b0, n_a = mk_block(genesis.block, 1, n_a), n_a.evolve()
b1, n_a = mk_block(b0, 2, n_a), n_a.evolve()
b2, n_b = mk_block(b0, 2, n_b), n_b.evolve()
b3, n_a = mk_block(b1, 3, n_a), n_a.evolve()
b4, n_a = mk_block(b3, 4, n_a), n_a.evolve()
b5, n_a = mk_block(b4, 5, n_a), n_a.evolve()
b0 = mk_block(genesis.block, 1, n_a)
b1 = mk_block(b0, 2, n_a)
b2 = mk_block(b0, 2, n_b)
b3 = mk_block(b1, 3, n_a)
b4 = mk_block(b3, 4, n_a)
b5 = mk_block(b4, 5, n_a)
for b in [b0, b1, b2, b3, b4, b5]:
peer.on_block(b)
self.assertEqual(peer.tip(), b5)
@ -474,8 +474,8 @@ class TestSyncFromCheckpoint(TestCase):
# And deliberately, add invalid blocks (b6 ~ b7):
fake_note = Note(sk=2, value=10)
b6, fake_note = mk_block(b2, 3, fake_note), fake_note.evolve()
b7, fake_note = mk_block(b6, 4, fake_note), fake_note.evolve()
b6 = mk_block(b2, 3, fake_note)
b7 = mk_block(b6, 4, fake_note)
apply_invalid_block_to_ledger_state(peer, b6)
apply_invalid_block_to_ledger_state(peer, b7)
# the tip shouldn't be changed.