Skip to content
This repository was archived by the owner on Aug 2, 2022. It is now read-only.

Commit 2b907ca

Browse files
authored
Merge pull request #10054 from EOSIO/qy-multiversion-test-failure-2.1.x
Fix multiversion test failure - merge 2.1.x
2 parents 1618a74 + c960fdc commit 2b907ca

File tree

3 files changed

+72
-34
lines changed

3 files changed

+72
-34
lines changed

.cicd/generate-pipeline.sh

+9-9
Original file line numberDiff line numberDiff line change
@@ -418,16 +418,10 @@ EOF
418418
IFS=$nIFS
419419
done
420420
IFS=$oIFS
421-
if [[ "$ROUND" != "$ROUNDS" ]]; then
422-
echo ' - wait'
423-
echo ''
424-
fi
425-
done
426-
# Execute multiversion test
427-
if [[ ! "$PINNED" == 'false' || "$SKIP_MULTIVERSION_TEST" == 'false' ]]; then
428-
cat <<EOF
421+
if [[ ! "$PINNED" == 'false' || "$SKIP_MULTIVERSION_TEST" == 'false' ]]; then
422+
cat <<EOF
429423
- label: ":pipeline: Multiversion Test"
430-
command:
424+
command:
431425
- "buildkite-agent artifact download build.tar.gz . --step ':ubuntu: Ubuntu 18.04 - Build' && tar -xzf build.tar.gz"
432426
- ./.cicd/test.sh .cicd/multiversion.sh
433427
env:
@@ -440,6 +434,12 @@ EOF
440434
441435
EOF
442436
fi
437+
if [[ "$ROUND" != "$ROUNDS" ]]; then
438+
echo ' - wait'
439+
echo ''
440+
fi
441+
done
442+
443443
# trigger eosio-lrt post pr
444444
if [[ -z $BUILDKITE_TRIGGERED_FROM_BUILD_ID && $TRIGGER_JOB == "true" ]]; then
445445
if ( [[ ! $PINNED == false ]] ); then

tests/Node.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,13 @@ def validateAccounts(self, accounts):
221221
raise
222222

223223
# pylint: disable=too-many-branches
224-
def getBlock(self, blockNum, silentErrors=False, exitOnError=False):
224+
def getBlock(self, blockNumOrId, silentErrors=False, exitOnError=False):
225225
"""Given a blockId will return block details."""
226-
assert(isinstance(blockNum, int))
226+
assert(isinstance(blockNumOrId, int) or isinstance(blockNumOrId, str))
227227
cmdDesc="get block"
228-
cmd="%s %d" % (cmdDesc, blockNum)
229-
msg="(block number=%s)" % (blockNum);
228+
numOrId="number" if isinstance(blockNumOrId, int) else "id"
229+
cmd="%s %s" % (cmdDesc, blockNumOrId)
230+
msg="(block %s=%s)" % (numOrId, blockNumOrId)
230231
return self.processCleosCmd(cmd, cmdDesc, silentErrors=silentErrors, exitOnError=exitOnError, exitMsg=msg)
231232

232233
def isBlockPresent(self, blockNum, blockType=BlockType.head):

tests/nodeos_multiple_version_protocol_feature_test.py

+58-21
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,21 @@ def restartNode(node: Node, chainArg=None, addSwapFlags=None, nodeosPath=None):
4545

4646
def shouldNodeContainPreactivateFeature(node):
4747
preactivateFeatureDigest = node.getSupportedProtocolFeatureDict()["PREACTIVATE_FEATURE"]["feature_digest"]
48-
assert preactivateFeatureDigest
48+
assert preactivateFeatureDigest, "preactivateFeatureDigest should not be empty"
4949
blockHeaderState = node.getLatestBlockHeaderState()
50+
assert blockHeaderState, "blockHeaderState should not be empty"
5051
activatedProtocolFeatures = blockHeaderState["activated_protocol_features"]["protocol_features"]
5152
return preactivateFeatureDigest in activatedProtocolFeatures
5253

54+
beginningOfProdTurnHead = 0
5355
def waitUntilBeginningOfProdTurn(node, producerName, timeout=30, sleepTime=0.4):
5456
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
5860
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)"
6063

6164
def waitForOneRound():
6265
time.sleep(24) # We have 4 producers for this test
@@ -120,17 +123,41 @@ def resumeBlockProductions():
120123
for node in allNodes:
121124
if not node.killed: node.processCurlCmd("producer", "resume", "")
122125

123-
def areNodesInSync(nodes:[Node]):
126+
def areNodesInSync(nodes:[Node], pauseAll=True, resumeAll=True):
124127
# 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 = []
127134
headBlockIds = []
128135
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 +++")
134161
# Before everything starts, all nodes (new version and old version) should be in sync
135162
assert areNodesInSync(allNodes), "Nodes are not in sync before preactivation"
136163

@@ -142,16 +169,26 @@ def areNodesInSync(nodes:[Node]):
142169
# Therefore, 1st node will be out of sync with 2nd, 3rd, and 4th node
143170
# After a round has passed though, 1st node will realize he's in minority fork and then join the other nodes
144171
# 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 +++")
145173
setValidityOfActTimeSubjRestriction(newNodes[1], "PREACTIVATE_FEATURE", False)
146174
setValidityOfActTimeSubjRestriction(newNodes[2], "PREACTIVATE_FEATURE", False)
147175

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+
150186
assert shouldNodeContainPreactivateFeature(newNodes[0]), "1st node should contain PREACTIVATE FEATURE"
151187
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 +++"
155192

156193
waitForOneRound()
157194

@@ -170,8 +207,8 @@ def areNodesInSync(nodes:[Node]):
170207
libBeforePreactivation = newNodes[0].getIrreversibleBlockNum()
171208
newNodes[0].activatePreactivateFeature()
172209

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"
175212
for node in newNodes: assert shouldNodeContainPreactivateFeature(node), "New node should contain PREACTIVATE_FEATURE"
176213

177214
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]):
181218
newNodes[2].getIrreversibleBlockNum() >= activatedBlockNum, \
182219
"2nd and 3rd node LIB should also be able to advance past the block that contains PREACTIVATE_FEATURE"
183220
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"
185222

186223
# Restart old node with newest version
187224
# Before we are migrating to new version, use --export-reversible-blocks as the old version

0 commit comments

Comments
 (0)