33# Distributed under the MIT software license, see the accompanying
44# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
6- from test_framework .test_framework import DashTestFramework
6+ import copy
7+ from io import BytesIO
78
9+ from test_framework .test_framework import DashTestFramework
10+ from test_framework .messages import (
11+ CBlock ,
12+ CFinalCommitmentPayload ,
13+ from_hex ,
14+ )
15+ from test_framework .util import assert_equal
816'''
917feature_llmq_dkgerrors.py
1018
@@ -20,8 +28,9 @@ def run_test(self):
2028 self .nodes [0 ].sporkupdate ("SPORK_17_QUORUM_DKG_ENABLED" , 0 )
2129 self .wait_for_sporks_same ()
2230
23- self .log .info ("Mine one quorum without simulating any errors" )
24- qh = self .mine_quorum ()
31+ qh = self .test_qc ()
32+
33+ self .log .info ("Mine one regular quorum with no invalid members is mined at this point" )
2534 self .assert_member_valid (qh , self .mninfo [0 ].proTxHash , True )
2635
2736 mninfos_valid = self .mninfo .copy ()[1 :]
@@ -70,6 +79,59 @@ def run_test(self):
7079 qh = self .mine_quorum (expected_contributions = 3 , expected_complaints = 0 , expected_justifications = 0 , expected_commitments = 2 , mninfos_valid = mninfos_valid )
7180 self .assert_member_valid (qh , self .mninfo [0 ].proTxHash , True )
7281
82+ def test_qc (self ):
83+ quorumHash = self .mine_quorum (skip_maturity = True )
84+ best = self .nodes [0 ].getbestblockhash ()
85+ block_hex = self .nodes [0 ].getblock (best , 0 )
86+
87+ block = from_hex (CBlock (), block_hex )
88+
89+ self .nodes [0 ].invalidateblock (best )
90+
91+ self .test_invalid (block , 'bad-qc-commitment-type' , lambda qc : (setattr (qc , 'llmqType' , 77 ), qc )[1 ])
92+ self .test_invalid (block , 'bad-qc-invalid' , lambda qc : (setattr (qc , 'llmqType' , 106 ), qc )[1 ])
93+ # TODO: test quorumIndex for rotation quorums
94+ # self.test_invalid(block, 'bad-qc-invalid', lambda qc : (setattr(qc, 'quorumIndex', 2), qc)[1])
95+ self .test_invalid (block , 'bad-qc-invalid' , lambda qc : (setattr (qc , 'quorumSig' , getattr (qc , 'membersSig' )), qc )[1 ])
96+ self .test_invalid (block , 'bad-qc-invalid' , lambda qc : (setattr (qc , 'membersSig' , getattr (qc , 'quorumSig' )), qc )[1 ])
97+ self .test_invalid (block , 'bad-qc-invalid' , lambda qc : (setattr (qc , 'quorumPublicKey' , b'\x00 ' * 48 ), qc )[1 ])
98+ # TODO: test quorumVvecHash
99+ # TODO: test signers
100+ # TODO: test validMembers
101+
102+ self .nodes [0 ].reconsiderblock (best )
103+ # Mine 8 (SIGN_HEIGHT_OFFSET) more blocks to make sure that the new quorum gets eligible for signing sessions
104+ self .generate (self .nodes [0 ], 8 , sync_fun = lambda : self .sync_blocks (self .nodes ))
105+
106+ return quorumHash
107+
108+ def test_invalid (self , original_block , error , transform ):
109+ node = self .nodes [0 ]
110+
111+ block = copy .deepcopy (original_block )
112+
113+ for tx in block .vtx :
114+ if tx .nType == 6 :
115+ qc_payload = CFinalCommitmentPayload ()
116+ qc_payload .deserialize (BytesIO (tx .vExtraPayload ))
117+ # test not only quorum 100, rotation quorums also and single-node quorum
118+ if qc_payload .commitment .llmqType == 100 :
119+ qc_payload .commitment = transform (qc_payload .commitment )
120+
121+ tx .vExtraPayload = qc_payload .serialize ()
122+ tx .rehash ()
123+
124+ def assert_submitblock (block , result_str_1 , result_str_2 = None ):
125+ block .hashMerkleRoot = block .calc_merkle_root ()
126+ block .solve ()
127+ result_str_2 = result_str_2 or 'duplicate-invalid'
128+ assert_equal (result_str_1 , node .submitblock (hexdata = block .serialize ().hex ()))
129+ assert_equal (result_str_2 , node .submitblock (hexdata = block .serialize ().hex ()))
130+
131+ # TODO: implement similar validation not only for submitblock but for p2p message
132+ assert_submitblock (block , error )
133+
134+
73135 def assert_member_valid (self , quorumHash , proTxHash , expectedValid ):
74136 q = self .nodes [0 ].quorum ('info' , 100 , quorumHash , True )
75137 for m in q ['members' ]:
0 commit comments