|
@@ -0,0 +1,78 @@
|
|
|
|
|
+# Section 3.4 from "Streamlet: Textbook Streamlined Blockchains"
|
|
|
|
|
+
|
|
|
|
|
+from block import Block
|
|
|
|
|
+from node import Node
|
|
|
|
|
+
|
|
|
|
|
+# Genesis block is generated.
|
|
|
|
|
+genesis_block = Block("⊥", 0, '⊥')
|
|
|
|
|
+
|
|
|
|
|
+# We create some nodes to participate in the Protocol.
|
|
|
|
|
+# There are in total n nodes numbered.
|
|
|
|
|
+node0 = Node(0, "clock", "node_password0", genesis_block)
|
|
|
|
|
+node1 = Node(1, "clock", "node_password1", genesis_block)
|
|
|
|
|
+node2 = Node(2, "clock", "node_password2", genesis_block)
|
|
|
|
|
+node3 = Node(3, "clock", "node_password3", genesis_block)
|
|
|
|
|
+node4 = Node(4, "clock", "node_password4", genesis_block)
|
|
|
|
|
+node5 = Node(5, "clock", "node_password5", genesis_block)
|
|
|
|
|
+
|
|
|
|
|
+nodes = [node0, node1, node2, node3, node4, node5]
|
|
|
|
|
+
|
|
|
|
|
+# We simulate some rounds to test consistency.
|
|
|
|
|
+epoch = 1
|
|
|
|
|
+
|
|
|
|
|
+# Nodes receive transactions and broacasts them between them.
|
|
|
|
|
+# node0 receives input and broadcasts it to rest nodes.
|
|
|
|
|
+node0.receive_transaction("tx0")
|
|
|
|
|
+node0.broadcast_transaction([node1, node2, node3, node4, node5], "tx0")
|
|
|
|
|
+# node1 receives input and broadcasts it to rest nodes.
|
|
|
|
|
+node1.receive_transaction("tx2")
|
|
|
|
|
+node1.broadcast_transaction([node0, node2, node3, node4, node5], "tx2")
|
|
|
|
|
+# node4 receives input and broadcasts it to rest nodes.
|
|
|
|
|
+node4.receive_transaction("tx3")
|
|
|
|
|
+node4.broadcast_transaction([node0, node1, node2, node3, node5], "tx3")
|
|
|
|
|
+
|
|
|
|
|
+# A random leader is selected.
|
|
|
|
|
+leader = nodes[hash(str(epoch))%len(nodes)]
|
|
|
|
|
+
|
|
|
|
|
+# Leader forms a block and broadcasts it.
|
|
|
|
|
+leader.propose_block(epoch, nodes)
|
|
|
|
|
+
|
|
|
|
|
+# Nodes vote on the block and broadcast their vote to rest nodes.
|
|
|
|
|
+for node in nodes:
|
|
|
|
|
+ node.vote_on_round_block(nodes)
|
|
|
|
|
+
|
|
|
|
|
+# We verify that all nodes have the same blockchain on round end.
|
|
|
|
|
+assert(node0.output() == node1.output() == node2.output() == node3.output() == node4.output() == node5.output())
|
|
|
|
|
+
|
|
|
|
|
+epoch = 2
|
|
|
|
|
+
|
|
|
|
|
+# We introduce a new node. Assumption: no history sync, a Node starts participating in next epoch.
|
|
|
|
|
+node6 = Node(6, "clock", "node_password5", node0.output()[-1])
|
|
|
|
|
+nodes.append(node6)
|
|
|
|
|
+
|
|
|
|
|
+# node3 receives input and broadcasts it to rest nodes.
|
|
|
|
|
+node3.receive_transaction("tx4")
|
|
|
|
|
+node3.broadcast_transaction([node0, node1, node2, node4, node5, node6], "tx4")
|
|
|
|
|
+# node5 receives input and broadcasts it to rest nodes.
|
|
|
|
|
+node5.receive_transaction("tx5")
|
|
|
|
|
+node5.broadcast_transaction([node0, node1, node2, node3, node4, node6], "tx5")
|
|
|
|
|
+# node6 receives input and broadcasts it to rest nodes.
|
|
|
|
|
+node6.receive_transaction("tx6")
|
|
|
|
|
+node6.broadcast_transaction([node0, node1, node2, node3, node4, node5], "tx6")
|
|
|
|
|
+
|
|
|
|
|
+# A random leader is selected.
|
|
|
|
|
+leader = nodes[hash(str(epoch))%len(nodes)]
|
|
|
|
|
+
|
|
|
|
|
+# Leader forms a block and broadcasts it.
|
|
|
|
|
+leader.propose_block(epoch, nodes)
|
|
|
|
|
+
|
|
|
|
|
+# Nodes vote on the block and broadcast their vote to rest nodes.
|
|
|
|
|
+for node in nodes:
|
|
|
|
|
+ node.vote_on_round_block(nodes)
|
|
|
|
|
+
|
|
|
|
|
+# We verify that all nodes have the same blockchain on round end.
|
|
|
|
|
+assert(node0.output() == node1.output() == node2.output() == node3.output() == node4.output() == node5.output())
|
|
|
|
|
+
|
|
|
|
|
+# Since node6 joined later, node0 output is a prefix or equal to node6 output.
|
|
|
|
|
+# Based on that, node6 output is a suffix of node0 output.
|
|
|
|
|
+assert(node0.output().blocks[-len(node6.output()):] == node6.output().blocks)
|