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

Added Privacy Test Scenario #3 to privacy-simple-network.py #10253

Merged
40 changes: 22 additions & 18 deletions tests/Cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ def connectGroup(group, producerNodes, bridgeNodes) :
if onlyBios:
self.nodes=[biosNode]

self.totalNodes = totalNodes

# ensure cluster node are inter-connected by ensuring everyone has block 1
Utils.Print("Cluster viability smoke test. Validate every cluster node has block 1. ")
if not self.waitOnClusterBlockNumSync(1):
Expand Down Expand Up @@ -1032,24 +1034,6 @@ def parseProducerKeys(configFile, nodeName):

return producerKeys

@staticmethod
def parseProducers(nodeNum):
"""Parse node config file for producers."""

configFile=Utils.getNodeConfigDir(nodeNum, "config.ini")
if Utils.Debug: Utils.Print("Parsing config file %s" % configFile)
configStr=None
with open(configFile, 'r') as f:
configStr=f.read()

pattern=r"^\s*producer-name\s*=\W*(\w+)\W*$"
producerMatches=re.findall(pattern, configStr, re.MULTILINE)
if producerMatches is None:
if Utils.Debug: Utils.Print("Failed to find producers.")
return None

return producerMatches

@staticmethod
def parseClusterKeys(totalNodes):
"""Parse cluster config file. Updates producer keys data members."""
Expand Down Expand Up @@ -1877,3 +1861,23 @@ def verifyInSync(self, sourceNodeNum=0, specificNodes=None):
if error:
self.reportInfo()
Utils.errorExit(error)

def getParticipantNum(self, nodeToIdentify):
num = 0
for node in self.nodes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for num, node in zip(range(len(self.nodes)), self.nodes) and dispense with manually initializing and incrementing num.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or alternatively we can do something like this:

range_nodes = range(len(self.nodes))
nodeDict = dict(zip(self.nodes, range_nodes)) | {self.biosNode : self.totalNodes}
return nodeDict[nodeToIndentify]

if node == nodeToIdentify:
return num
num += 1
assert nodeToIdentify == self.biosNode
return self.totalNodes

def getProducingNodeIndex(self, blockProducer):
featureProdNum = 0
while featureProdNum < pnodes:
if blockProducer in self.nodes[featureProdNum].getProducers():
return featureProdNum

featureProdNum += 1

assert blockProducer in self.biosNode.getProducers(), "Checked all nodes but could not find producer: {}".format(blockProducer)
return "bios"
94 changes: 75 additions & 19 deletions tests/Node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,7 +1200,7 @@ def getBlockProducerByNum(self, blockNum, timeout=None, waitForBlock=True, exitO
block=self.getBlock(blockNum, exitOnError=exitOnError)
return Node.getBlockAttribute(block, "producer", blockNum, exitOnError=exitOnError)

def getBlockProducer(self, timeout=None, waitForBlock=True, exitOnError=True, blockType=BlockType.head):
def getBlockProducer(self, timeout=None, exitOnError=True, blockType=BlockType.head):
blockNum=self.getBlockNum(blockType=blockType)
block=self.getBlock(blockNum, exitOnError=exitOnError, blockType=blockType)
return Node.getBlockAttribute(block, "producer", blockNum, exitOnError=exitOnError)
Expand Down Expand Up @@ -1453,7 +1453,8 @@ def isDesiredProdTurn():
return beginningOfProdTurnHead

# Require producer_api_plugin
def activateFeatures(self, features):
def activateFeatures(self, features, blocksToAdvance=2):
assert blocksToAdvance >= 0
featureDigests = []
for feature in features:
protocolFeatureDigestDict = self.getSupportedProtocolFeatureDict()
Expand All @@ -1465,16 +1466,44 @@ def activateFeatures(self, features):
self.scheduleProtocolFeatureActivations(featureDigests)

# Wait for the next block to be produced so the scheduled protocol feature is activated
assert self.waitForHeadToAdvance(blocksToAdvance=2), print("ERROR: TIMEOUT WAITING FOR activating features: {}".format(",".join(features)))
assert self.waitForHeadToAdvance(blocksToAdvance=blocksToAdvance), print("ERROR: TIMEOUT WAITING FOR activating features: {}".format(",".join(features)))

def activateAndVerifyFeatures(self, features):
self.activateFeatures(features, blocksToAdvance=0)
headBlockNum = self.getBlockNum()
blockNum = headBlockNum
producers = {}
lastProducer = None
while True:
block = self.getBlock(blockNum)
blockHeaderState = self.getBlockHeaderState(blockNum)
if self.containsFeatures(features, blockHeaderState):
return

producer = block["producer"]
producers[producer] += 1
assert lastProducer != producer or producers[producer] == 1, \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire assert doesn't seem very useful. It will trigger immediately in a single producer network, and never trigger in a multi-producer network because the second assert below will always trigger first.

"We have already cycled through a complete cycle, so feature should have been set by now. \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about "Producer schedule cycle completed and feature has not been set. Initial block num: {}, current block num: {}"

Initial block num: {}, looking at block num: {}".format(headBlockNum, blockNum)

# feature should be in block for this node's producers, if it is at least 2 blocks after we sent the activate
minBlocksForGuarantee = 2
assert producer not in self.getProducers() or blockNum - headBlockNum < minBlocksForGuarantee, \
"It is {} blocks past the block when we activated the features and block num: {} was produced by this \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for colon after "block num" in message.

node, so features should have been set."
Copy link
Contributor

@jgiszczak jgiszczak Apr 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing format call for block num substitutions.

self.waitForBlock(blockNum + 1)
blockNum = self.getBlockNum()



# Require producer_api_plugin
def activatePreactivateFeature(self):
return self.activateFeatures(["PREACTIVATE_FEATURE"])

def containsFeatures(self, features):
def containsFeatures(self, features, blockHeaderState=None):
protocolFeatureDict = self.getSupportedProtocolFeatureDict()
blockHeaderState = self.getLatestBlockHeaderState()
assert blockHeaderState, "blockHeaderState should not be empty"
if blockHeaderState is None:
blockHeaderState = self.getLatestBlockHeaderState()
for feature in features:
featureDigest = protocolFeatureDict[feature]["feature_digest"]
assert featureDigest, "{}'s Digest should not be empty".format(feature)
Expand Down Expand Up @@ -1520,20 +1549,26 @@ def preactivateAllBuiltinProtocolFeature(self):

def getLatestBlockHeaderState(self):
headBlockNum = self.getHeadBlockNum()
for i in range(10):
cmdDesc = "get block {} --header-state".format(headBlockNum)
latestBlockHeaderState = self.processCleosCmd(cmdDesc, cmdDesc)
Utils.Print("block num: {}, block state: {}, head: {}".format(headBlockNum, latestBlockHeaderState, self.getHeadBlockNum()))
if latestBlockHeaderState:
return latestBlockHeaderState
time.sleep(1)
return None

def getActivatedProtocolFeatures(self):
latestBlockHeaderState = self.getLatestBlockHeaderState()
if "activated_protocol_features" not in latestBlockHeaderState or "protocol_features" not in latestBlockHeaderState["activated_protocol_features"]:
return self.getBlockHeaderState(headBlockNum)

def getBlockHeaderState(self, blockNum, errorOnNone=True):
cmdDesc = "get block {} --header-state".format(blockNum)
blockHeaderState = self.processCleosCmd(cmdDesc, cmdDesc)
if blockHeaderState is None and errorOnNone:
info = self.getInfo()
lib = info["last_irreversible_block_num"]
head = info["head_block_num"]
assert head == lib + 1, "getLatestBlockHeaderState failed to retrieve the latest block. This should be investigated."
Utils.errorExit("Called getLatestBlockHeaderState, which can only retrieve blocks in reversible database, but the test setup only has one producer so there" +
" is only 1 block in the reversible database. Test should be redesigned to aquire this information via another interface.")
return blockHeaderState

def getActivatedProtocolFeatures(self, blockHeaderState=None):
if blockHeaderState is None:
blockHeaderState = self.getLatestBlockHeaderState()
if "activated_protocol_features" not in blockHeaderState or "protocol_features" not in blockHeaderState["activated_protocol_features"]:
Utils.errorExit("getLatestBlockHeaderState did not return expected output, should contain [\"activated_protocol_features\"][\"protocol_features\"]: {}".format(latestBlockHeaderState))
return latestBlockHeaderState["activated_protocol_features"]["protocol_features"]
return blockHeaderState["activated_protocol_features"]["protocol_features"]

def modifyBuiltinPFSubjRestrictions(self, featureCodename, subjectiveRestriction={}):
jsonPath = os.path.join(Utils.getNodeConfigDir(self.nodeId),
Expand Down Expand Up @@ -1687,3 +1722,24 @@ def waitForIrreversibleBlockProducedBy(self, producer, startBlockNum=0, retry=10
retry = retry - 1
startBlockNum = latestBlockNum + 1
return False

@staticmethod
def parseProducers(nodeNum):
dimas1185 marked this conversation as resolved.
Show resolved Hide resolved
"""Parse node config file for producers."""

configFile=Utils.getNodeConfigDir(nodeNum, "config.ini")
if Utils.Debug: Utils.Print("Parsing config file %s" % configFile)
configStr=None
with open(configFile, 'r') as f:
configStr=f.read()

pattern=r"^\s*producer-name\s*=\W*(\w+)\W*$"
producerMatches=re.findall(pattern, configStr, re.MULTILINE)
if producerMatches is None:
if Utils.Debug: Utils.Print("Failed to find producers.")
return None

return producerMatches

def getProducers(self):
return Node.parseProducers(self.nodeId)
2 changes: 1 addition & 1 deletion tests/nodeos_forked_chain_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def getMinHeadAndLib(prodNodes):
producers=[]
for i in range(0, totalNodes):
node=cluster.getNode(i)
node.producers=Cluster.parseProducers(i)
node.producers=node.getProducers()
numProducers=len(node.producers)
Print("node has producers=%s" % (node.producers))
if numProducers==0:
Expand Down
2 changes: 1 addition & 1 deletion tests/nodeos_high_transaction_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
allNodes=cluster.getNodes()
for i in range(0, totalNodes):
node=allNodes[i]
nodeProducers=Cluster.parseProducers(i)
nodeProducers=node.getProducers()
numProducers=len(nodeProducers)
Print("node has producers=%s" % (nodeProducers))
if numProducers==0:
Expand Down
2 changes: 1 addition & 1 deletion tests/nodeos_short_fork_take_over_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def getMinHeadAndLib(prodNodes):
producers=[]
for i in range(0, totalNodes):
node=cluster.getNode(i)
node.producers=Cluster.parseProducers(i)
node.producers=node.getProducers()
numProducers=len(node.producers)
Print("node has producers=%s" % (node.producers))
if numProducers==0:
Expand Down
2 changes: 1 addition & 1 deletion tests/nodeos_voting_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def verifyProductionRounds(trans, node, prodsActive, rounds):

for i in range(0, totalNodes):
node=cluster.getNode(i)
node.producers=Cluster.parseProducers(i)
node.producers=node.getProducers()
for prod in node.producers:
trans=node.regproducer(cluster.defProducerAccounts[prod], "http::/mysite.com", 0, waitForTransBlock=False, exitOnError=True)

Expand Down
Loading