Skip to content

Commit adc101a

Browse files
committed
Implement quorum commitment merkle root tests in dip4-coinbasemerkleroots.py
1 parent 40ad06e commit adc101a

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

qa/rpc-tests/dip4-coinbasemerkleroots.py

+147
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,79 @@ def run_test(self):
8181
# When comparing genesis and best block, we shouldn't see the previously added and then deleted MN
8282
mnList = self.test_getmnlistdiff(null_hash, self.nodes[0].getbestblockhash(), {}, [], expectedUpdated2)
8383

84+
#############################
85+
# Now start testing quorum commitment merkle roots
86+
87+
height = self.nodes[0].getblockcount()
88+
# Test DIP8 activation once with a pre-existing quorum and once without (we don't know in which order it will activate on mainnet)
89+
self.test_dip8_quorum_merkle_root_activation(True)
90+
for n in self.nodes:
91+
n.invalidateblock(n.getblockhash(height + 1))
92+
first_quorum = self.test_dip8_quorum_merkle_root_activation(False)
93+
94+
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
95+
self.wait_for_sporks_same()
96+
97+
# Verify that the first quorum appears in MNLISTDIFF
98+
expectedDeleted = []
99+
expectedNew = [QuorumId(100, int(first_quorum, 16))]
100+
quorumList = self.test_getmnlistdiff_quorums(null_hash, self.nodes[0].getbestblockhash(), {}, expectedDeleted, expectedNew)
101+
baseBlockHash = self.nodes[0].getbestblockhash()
102+
103+
second_quorum = self.mine_quorum()
104+
105+
# Verify that the second quorum appears in MNLISTDIFF
106+
expectedDeleted = []
107+
expectedNew = [QuorumId(100, int(second_quorum, 16))]
108+
quorums_before_third = self.test_getmnlistdiff_quorums(baseBlockHash, self.nodes[0].getbestblockhash(), quorumList, expectedDeleted, expectedNew)
109+
block_before_third = self.nodes[0].getbestblockhash()
110+
111+
third_quorum = self.mine_quorum()
112+
113+
# Verify that the first quorum is deleted and the third quorum is added in MNLISTDIFF (the first got inactive)
114+
expectedDeleted = [QuorumId(100, int(first_quorum, 16))]
115+
expectedNew = [QuorumId(100, int(third_quorum, 16))]
116+
self.test_getmnlistdiff_quorums(block_before_third, self.nodes[0].getbestblockhash(), quorums_before_third, expectedDeleted, expectedNew)
117+
118+
# Verify that the diff between genesis and best block is the current active set (second and third quorum)
119+
expectedDeleted = []
120+
expectedNew = [QuorumId(100, int(second_quorum, 16)), QuorumId(100, int(third_quorum, 16))]
121+
self.test_getmnlistdiff_quorums(null_hash, self.nodes[0].getbestblockhash(), {}, expectedDeleted, expectedNew)
122+
123+
# Now verify that diffs are correct around the block that mined the third quorum.
124+
# This tests the logic in CalcCbTxMerkleRootQuorums, which has to manually add the commitment from the current
125+
# block
126+
mined_in_block = self.nodes[0].quorum("info", 100, third_quorum)["minedBlock"]
127+
prev_block = self.nodes[0].getblock(mined_in_block)["previousblockhash"]
128+
prev_block2 = self.nodes[0].getblock(prev_block)["previousblockhash"]
129+
next_block = self.nodes[0].getblock(mined_in_block)["nextblockhash"]
130+
next_block2 = self.nodes[0].getblock(mined_in_block)["nextblockhash"]
131+
# The 2 block before the quorum was mined should both give an empty diff
132+
expectedDeleted = []
133+
expectedNew = []
134+
self.test_getmnlistdiff_quorums(block_before_third, prev_block2, quorums_before_third, expectedDeleted, expectedNew)
135+
self.test_getmnlistdiff_quorums(block_before_third, prev_block, quorums_before_third, expectedDeleted, expectedNew)
136+
# The block in which the quorum was mined and the 2 after that should all give the same diff
137+
expectedDeleted = [QuorumId(100, int(first_quorum, 16))]
138+
expectedNew = [QuorumId(100, int(third_quorum, 16))]
139+
quorums_with_third = self.test_getmnlistdiff_quorums(block_before_third, mined_in_block, quorums_before_third, expectedDeleted, expectedNew)
140+
self.test_getmnlistdiff_quorums(block_before_third, next_block, quorums_before_third, expectedDeleted, expectedNew)
141+
self.test_getmnlistdiff_quorums(block_before_third, next_block2, quorums_before_third, expectedDeleted, expectedNew)
142+
# A diff between the two block that happened after the quorum was mined should give an empty diff
143+
expectedDeleted = []
144+
expectedNew = []
145+
self.test_getmnlistdiff_quorums(mined_in_block, next_block, quorums_with_third, expectedDeleted, expectedNew)
146+
self.test_getmnlistdiff_quorums(mined_in_block, next_block2, quorums_with_third, expectedDeleted, expectedNew)
147+
self.test_getmnlistdiff_quorums(next_block, next_block2, quorums_with_third, expectedDeleted, expectedNew)
148+
149+
# Using the same block for baseBlockHash and blockHash should give empty diffs
150+
self.test_getmnlistdiff_quorums(prev_block, prev_block, quorums_before_third, expectedDeleted, expectedNew)
151+
self.test_getmnlistdiff_quorums(prev_block2, prev_block2, quorums_before_third, expectedDeleted, expectedNew)
152+
self.test_getmnlistdiff_quorums(mined_in_block, mined_in_block, quorums_with_third, expectedDeleted, expectedNew)
153+
self.test_getmnlistdiff_quorums(next_block, next_block, quorums_with_third, expectedDeleted, expectedNew)
154+
self.test_getmnlistdiff_quorums(next_block2, next_block2, quorums_with_third, expectedDeleted, expectedNew)
155+
156+
84157
def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList, expectedDeleted, expectedUpdated):
85158
d = self.test_getmnlistdiff_base(baseBlockHash, blockHash)
86159

@@ -107,6 +180,34 @@ def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList, expectedDelet
107180

108181
return newMNList
109182

183+
def test_getmnlistdiff_quorums(self, baseBlockHash, blockHash, baseQuorumList, expectedDeleted, expectedNew):
184+
d = self.test_getmnlistdiff_base(baseBlockHash, blockHash)
185+
186+
assert_equal(set(d.deletedQuorums), set(expectedDeleted))
187+
assert_equal(set([QuorumId(e.llmqType, e.quorumHash) for e in d.newQuorums]), set(expectedNew))
188+
189+
newQuorumList = baseQuorumList.copy()
190+
191+
for e in d.deletedQuorums:
192+
newQuorumList.pop(e)
193+
194+
for e in d.newQuorums:
195+
newQuorumList[QuorumId(e.llmqType, e.quorumHash)] = e
196+
197+
cbtx = CCbTx()
198+
cbtx.deserialize(BytesIO(d.cbTx.vExtraPayload))
199+
200+
if cbtx.version >= 2:
201+
hashes = []
202+
for qc in newQuorumList.values():
203+
hashes.append(hash256(qc.serialize()))
204+
hashes.sort()
205+
merkleRoot = CBlock.get_merkle_root(hashes)
206+
assert_equal(merkleRoot, cbtx.merkleRootQuorums)
207+
208+
return newQuorumList
209+
210+
110211
def test_getmnlistdiff_base(self, baseBlockHash, blockHash):
111212
hexstr = self.nodes[0].getblockheader(blockHash, False)
112213
header = FromHex(CBlockHeader(), hexstr)
@@ -128,9 +229,55 @@ def test_getmnlistdiff_base(self, baseBlockHash, blockHash):
128229
assert_equal(d2["cbTx"], d.cbTx.serialize().hex())
129230
assert_equal(set([int(e, 16) for e in d2["deletedMNs"]]), set(d.deletedMNs))
130231
assert_equal(set([int(e["proRegTxHash"], 16) for e in d2["mnList"]]), set([e.proRegTxHash for e in d.mnList]))
232+
assert_equal(set([QuorumId(e["llmqType"], int(e["quorumHash"], 16)) for e in d2["deletedQuorums"]]), set(d.deletedQuorums))
233+
assert_equal(set([QuorumId(e["llmqType"], int(e["quorumHash"], 16)) for e in d2["newQuorums"]]), set([QuorumId(e.llmqType, e.quorumHash) for e in d.newQuorums]))
131234

132235
return d
133236

237+
def test_dip8_quorum_merkle_root_activation(self, with_initial_quorum):
238+
if with_initial_quorum:
239+
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
240+
self.wait_for_sporks_same()
241+
242+
# Mine one quorum before dip8 is activated
243+
self.mine_quorum()
244+
245+
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 4070908800)
246+
self.wait_for_sporks_same()
247+
248+
cbtx = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 2)["tx"][0]
249+
assert(cbtx["cbTx"]["version"] == 1)
250+
251+
assert(self.nodes[0].getblockchaininfo()["bip9_softforks"]["dip0008"]["status"] != "active")
252+
253+
while self.nodes[0].getblockchaininfo()["bip9_softforks"]["dip0008"]["status"] != "active":
254+
self.nodes[0].generate(4)
255+
self.nodes[0].generate(1)
256+
sync_blocks(self.nodes)
257+
258+
# Assert that merkleRootQuorums is present and 0 (we have no quorums yet)
259+
cbtx = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 2)["tx"][0]
260+
assert_equal(cbtx["cbTx"]["version"], 2)
261+
assert("merkleRootQuorums" in cbtx["cbTx"])
262+
merkleRootQuorums = int(cbtx["cbTx"]["merkleRootQuorums"], 16)
263+
264+
if with_initial_quorum:
265+
assert(merkleRootQuorums != 0)
266+
else:
267+
assert_equal(merkleRootQuorums, 0)
268+
269+
set_mocktime(get_mocktime() + 1)
270+
set_node_times(self.nodes, get_mocktime())
271+
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
272+
self.wait_for_sporks_same()
273+
274+
# Mine quorum and verify that merkleRootQuorums has changed
275+
quorum = self.mine_quorum()
276+
cbtx = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 2)["tx"][0]
277+
assert(int(cbtx["cbTx"]["merkleRootQuorums"], 16) != merkleRootQuorums)
278+
279+
return quorum
280+
134281
def confirm_mns(self):
135282
while True:
136283
diff = self.nodes[0].protx("diff", 1, self.nodes[0].getblockcount())

0 commit comments

Comments
 (0)