@@ -45,18 +45,21 @@ def restartNode(node: Node, chainArg=None, addSwapFlags=None, nodeosPath=None):
45
45
46
46
def shouldNodeContainPreactivateFeature (node ):
47
47
preactivateFeatureDigest = node .getSupportedProtocolFeatureDict ()["PREACTIVATE_FEATURE" ]["feature_digest" ]
48
- assert preactivateFeatureDigest
48
+ assert preactivateFeatureDigest , "preactivateFeatureDigest should not be empty"
49
49
blockHeaderState = node .getLatestBlockHeaderState ()
50
+ assert blockHeaderState , "blockHeaderState should not be empty"
50
51
activatedProtocolFeatures = blockHeaderState ["activated_protocol_features" ]["protocol_features" ]
51
52
return preactivateFeatureDigest in activatedProtocolFeatures
52
53
54
+ beginningOfProdTurnHead = 0
53
55
def waitUntilBeginningOfProdTurn (node , producerName , timeout = 30 , sleepTime = 0.4 ):
54
56
def isDesiredProdTurn ():
55
- headBlockNum = node .getHeadBlockNum ()
56
- res = node .getBlock (headBlockNum )["producer" ] == producerName and \
57
- node .getBlock (headBlockNum - 1 )["producer" ] != producerName
57
+ beginningOfProdTurnHead = node .getHeadBlockNum ()
58
+ res = node .getBlock (beginningOfProdTurnHead )["producer" ] == producerName and \
59
+ node .getBlock (beginningOfProdTurnHead - 1 )["producer" ] != producerName
58
60
return res
59
- Utils .waitForTruth (isDesiredProdTurn , timeout , sleepTime )
61
+ ret = Utils .waitForTruth (isDesiredProdTurn , timeout , sleepTime )
62
+ assert ret != None , "Expected producer to arrive within 19 seconds (with 3 other producers)"
60
63
61
64
def waitForOneRound ():
62
65
time .sleep (24 ) # We have 4 producers for this test
@@ -120,17 +123,41 @@ def resumeBlockProductions():
120
123
for node in allNodes :
121
124
if not node .killed : node .processCurlCmd ("producer" , "resume" , "" )
122
125
123
- def areNodesInSync (nodes :[Node ]):
126
+ def areNodesInSync (nodes :[Node ], pauseAll = True , resumeAll = True ):
124
127
# Pause all block production to ensure the head is not moving
125
- pauseBlockProductions ()
126
- time .sleep (2 ) # Wait for some time to ensure all blocks are propagated
128
+ if pauseAll :
129
+ pauseBlockProductions ()
130
+ time .sleep (2 ) # Wait for some time to ensure all blocks are propagated
131
+
132
+ # Get current head block number and IDs for each producer
133
+ headBlockNums = []
127
134
headBlockIds = []
128
135
for node in nodes :
129
- headBlockId = node .getInfo ()["head_block_id" ]
130
- headBlockIds .append (headBlockId )
131
- resumeBlockProductions ()
132
- return len (set (headBlockIds )) == 1
133
-
136
+ hb = node .getInfo ()
137
+ headBlockNums .append (hb ["head_block_num" ])
138
+ headBlockIds .append (hb ["head_block_id" ])
139
+ Utils .Print ("node {}, head block id: {}, num: {}" .format (node .nodeId , hb ["head_block_id" ], hb ["head_block_num" ]))
140
+ assert len (set (headBlockNums )) == len (set (headBlockIds )), "Different block IDs have the same block numbers, thus nodes are not in sync"
141
+ # Check if each node has head blocks from other producers
142
+ inSync = True
143
+ if len (set (headBlockNums )) != 1 :
144
+ def nodeHasBlocks (node , blockIds , blockNums ):
145
+ for blkId , blkNum in zip (blockIds , blockNums ):
146
+ assert node .waitForBlock (blkNum , timeout = 3 ) != None , "Expected to find block {}, but only reached {}" .format (blkNum , node .getInfo ()["head_block_num" ])
147
+ if node .getBlock (blkId ) is None :
148
+ Utils .Print ("node {} does not have block Id: {} (num: {})" .format (node .nodeId , blkId , blkNum ))
149
+ return False
150
+ return True
151
+ for node in nodes :
152
+ if not nodeHasBlocks (node , headBlockIds , headBlockNums ):
153
+ inSync = False
154
+ break
155
+
156
+ if resumeAll :
157
+ resumeBlockProductions ()
158
+ return inSync
159
+
160
+ Utils .Print ("+++ Nodes are in sync before preactivation +++" )
134
161
# Before everything starts, all nodes (new version and old version) should be in sync
135
162
assert areNodesInSync (allNodes ), "Nodes are not in sync before preactivation"
136
163
@@ -142,16 +169,26 @@ def areNodesInSync(nodes:[Node]):
142
169
# Therefore, 1st node will be out of sync with 2nd, 3rd, and 4th node
143
170
# After a round has passed though, 1st node will realize he's in minority fork and then join the other nodes
144
171
# Hence, the PREACTIVATE_FEATURE that was previously activated will be dropped and all of the nodes should be in sync
172
+ Utils .Print ("+++ 1st Node should contain PREACTIVATE FEATURE +++" )
145
173
setValidityOfActTimeSubjRestriction (newNodes [1 ], "PREACTIVATE_FEATURE" , False )
146
174
setValidityOfActTimeSubjRestriction (newNodes [2 ], "PREACTIVATE_FEATURE" , False )
147
175
148
- waitUntilBeginningOfProdTurn (newNodes [0 ], "defproducera" )
149
- newNodes [0 ].activatePreactivateFeature ()
176
+ for i in range (3 ):
177
+ Utils .Print ("1st node tries activatePreactivateFeature time(s): {}" .format (i + 1 ))
178
+ # 1st node waits for the start of the production turn each time it tries activatePreactivateFeature()
179
+ waitUntilBeginningOfProdTurn (newNodes [0 ], "defproducera" )
180
+ newNodes [0 ].activatePreactivateFeature ()
181
+ if shouldNodeContainPreactivateFeature (newNodes [0 ]):
182
+ break
183
+ diff = newNodes [0 ].getInfo ()["head_block_num" ] - beginningOfProdTurnHead
184
+ assert diff >= 12 , "1st node should contain PREACTIVATE FEATURE since we set it during its production window"
185
+
150
186
assert shouldNodeContainPreactivateFeature (newNodes [0 ]), "1st node should contain PREACTIVATE FEATURE"
151
187
assert not (shouldNodeContainPreactivateFeature (newNodes [1 ]) or shouldNodeContainPreactivateFeature (newNodes [2 ])), \
152
- "2nd and 3rd node should not contain PREACTIVATE FEATURE"
153
- assert areNodesInSync ([newNodes [1 ], newNodes [2 ], oldNode ]), "2nd, 3rd and 4th node should be in sync"
154
- assert not areNodesInSync (allNodes ), "1st node should be out of sync with the rest nodes"
188
+ "2nd and 3rd node should not contain PREACTIVATE FEATURE"
189
+ Utils .Print ("+++ 2nd, 3rd and 4th node should be in sync, and 1st node should be out of sync +++" )
190
+ assert areNodesInSync ([newNodes [1 ], newNodes [2 ], oldNode ], pauseAll = True , resumeAll = False ), "2nd, 3rd and 4th node should be in sync"
191
+ assert not areNodesInSync (allNodes , pauseAll = False , resumeAll = True ), "+++ 1st node should be out of sync with the rest nodes +++"
155
192
156
193
waitForOneRound ()
157
194
@@ -170,8 +207,8 @@ def areNodesInSync(nodes:[Node]):
170
207
libBeforePreactivation = newNodes [0 ].getIrreversibleBlockNum ()
171
208
newNodes [0 ].activatePreactivateFeature ()
172
209
173
- assert areNodesInSync (newNodes ), "New nodes should be in sync"
174
- assert not areNodesInSync (allNodes ), "Nodes should not be in sync after preactivation"
210
+ assert areNodesInSync (newNodes , pauseAll = True , resumeAll = False ), "New nodes should be in sync"
211
+ assert not areNodesInSync (allNodes , pauseAll = False , resumeAll = True ), "Nodes should not be in sync after preactivation"
175
212
for node in newNodes : assert shouldNodeContainPreactivateFeature (node ), "New node should contain PREACTIVATE_FEATURE"
176
213
177
214
activatedBlockNum = newNodes [0 ].getHeadBlockNum () # The PREACTIVATE_FEATURE should have been activated before or at this block num
@@ -181,7 +218,7 @@ def areNodesInSync(nodes:[Node]):
181
218
newNodes [2 ].getIrreversibleBlockNum () >= activatedBlockNum , \
182
219
"2nd and 3rd node LIB should also be able to advance past the block that contains PREACTIVATE_FEATURE"
183
220
assert oldNode .getIrreversibleBlockNum () <= libBeforePreactivation , \
184
- "4th node LIB should stuck on LIB before PREACTIVATE_FEATURE is activated"
221
+ "4th node LIB should be stuck on LIB before PREACTIVATE_FEATURE is activated"
185
222
186
223
# Restart old node with newest version
187
224
# Before we are migrating to new version, use --export-reversible-blocks as the old version
0 commit comments