1313// You should have received a copy of the GNU General Public License
1414// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515use std:: collections:: HashMap ;
16+ use std:: time:: SystemTime ;
1617
1718use clarity:: types:: chainstate:: {
18- ConsensusHash , StacksAddress , StacksBlockId , StacksPrivateKey , StacksPublicKey ,
19+ BurnchainHeaderHash , ConsensusHash , StacksAddress , StacksBlockId , StacksPrivateKey ,
20+ StacksPublicKey ,
1921} ;
2022use clarity:: util:: hash:: Hash160 ;
2123use clarity:: util:: secp256k1:: MessageSignature ;
@@ -32,7 +34,7 @@ use crate::v0::signer_state::LocalStateMachine;
3234#[ test]
3335fn check_capitulate_miner_view ( ) {
3436 let mut address_weights = HashMap :: new ( ) ;
35- for _ in 0 ..5 {
37+ for _ in 0 ..10 {
3638 let stacks_address = StacksAddress :: p2pkh (
3739 false ,
3840 & StacksPublicKey :: from_private ( & StacksPrivateKey :: random ( ) ) ,
@@ -43,17 +45,23 @@ fn check_capitulate_miner_view() {
4345 let active_signer_protocol_version = 0 ;
4446 let local_supported_signer_protocol_version = 0 ;
4547 let burn_block = ConsensusHash ( [ 0x55 ; 20 ] ) ;
46- let burn_block_height = 100 ;
4748 let parent_tenure_id = ConsensusHash ( [ 0x22 ; 20 ] ) ;
4849 let parent_tenure_last_block = StacksBlockId ( [ 0x33 ; 32 ] ) ;
4950 let parent_tenure_last_block_height = 1 ;
51+
52+ let old_miner_tenure_id = ConsensusHash ( [ 0x01 ; 20 ] ) ;
53+ let new_miner_tenure_id = ConsensusHash ( [ 0x00 ; 20 ] ) ;
54+
55+ let burn_block_height = 100 ;
56+
5057 let old_miner = StateMachineUpdateMinerState :: ActiveMiner {
5158 current_miner_pkh : Hash160 ( [ 0xab ; 20 ] ) ,
52- tenure_id : ConsensusHash ( [ 0x44 ; 20 ] ) ,
59+ tenure_id : old_miner_tenure_id ,
5360 parent_tenure_id,
5461 parent_tenure_last_block,
5562 parent_tenure_last_block_height,
5663 } ;
64+ // Make sure the old update still has the newer burn block height
5765 let old_update = StateMachineUpdateMessage :: new (
5866 active_signer_protocol_version,
5967 local_supported_signer_protocol_version,
@@ -86,35 +94,21 @@ fn check_capitulate_miner_view() {
8694 StateMachineUpdateContent :: V0 {
8795 burn_block,
8896 burn_block_height,
89- current_miner ,
97+ ..
9098 } ,
9199 ..
92100 } = local_update. clone ( )
93101 else {
94102 panic ! ( "Unexpected state machine update message version" ) ;
95103 } ;
96104 // Let's create a new miner view
97- let new_tenure_id = ConsensusHash ( [ 0x00 ; 20 ] ) ;
98-
99- let db_path = tmp_db_path ( ) ;
100- let mut db = SignerDb :: new ( db_path) . expect ( "Failed to create signer db" ) ;
101- let ( mut block_info_1, _block_proposal) = create_block_override ( |b| {
102- b. block . header . consensus_hash = new_tenure_id;
103- b. block . header . miner_signature = MessageSignature ( [ 0x01 ; 65 ] ) ;
104- b. block . header . chain_length = 1 ;
105- b. burn_height = burn_block_height;
106- } ) ;
107-
108- db. insert_block ( & block_info_1) . unwrap ( ) ;
109105 let new_miner = StateMachineUpdateMinerState :: ActiveMiner {
110106 current_miner_pkh : Hash160 ( [ 0x00 ; 20 ] ) ,
111- tenure_id : new_tenure_id ,
107+ tenure_id : new_miner_tenure_id ,
112108 parent_tenure_id,
113109 parent_tenure_last_block,
114110 parent_tenure_last_block_height,
115111 } ;
116-
117- // Let's update only our own view: the evaluator will tell me to revert my viewpoint to the old miner
118112 let new_update = StateMachineUpdateMessage :: new (
119113 active_signer_protocol_version,
120114 local_supported_signer_protocol_version,
@@ -126,6 +120,43 @@ fn check_capitulate_miner_view() {
126120 )
127121 . unwrap ( ) ;
128122
123+ // Update the database to have both the burn blocks corresponding to the tenure ids
124+ // and both tenures have a locally accepted block
125+ let db_path = tmp_db_path ( ) ;
126+ let mut db = SignerDb :: new ( db_path) . expect ( "Failed to create signer db" ) ;
127+ // Make sure both burn block corresponding to the tenure id's exist in our DB.
128+ db. insert_burn_block (
129+ & BurnchainHeaderHash ( [ 0u8 ; 32 ] ) ,
130+ & old_miner_tenure_id,
131+ burn_block_height. saturating_sub ( 1 ) ,
132+ & SystemTime :: now ( ) ,
133+ & BurnchainHeaderHash ( [ 1u8 ; 32 ] ) ,
134+ )
135+ . unwrap ( ) ;
136+ db. insert_burn_block (
137+ & BurnchainHeaderHash ( [ 0u8 ; 32 ] ) ,
138+ & new_miner_tenure_id,
139+ burn_block_height,
140+ & SystemTime :: now ( ) ,
141+ & BurnchainHeaderHash ( [ 1u8 ; 32 ] ) ,
142+ )
143+ . unwrap ( ) ;
144+ let ( mut block_info_1, _block_proposal) = create_block_override ( |b| {
145+ b. block . header . consensus_hash = old_miner_tenure_id;
146+ b. block . header . miner_signature = MessageSignature ( [ 0x02 ; 65 ] ) ;
147+ b. block . header . chain_length = 1 ;
148+ b. burn_height = burn_block_height. saturating_sub ( 1 ) ;
149+ } ) ;
150+ db. insert_block ( & block_info_1) . unwrap ( ) ;
151+
152+ let ( mut block_info_2, _block_proposal) = create_block_override ( |b| {
153+ b. block . header . consensus_hash = new_miner_tenure_id;
154+ b. block . header . miner_signature = MessageSignature ( [ 0x01 ; 65 ] ) ;
155+ b. block . header . chain_length = 1 ;
156+ b. burn_height = burn_block_height;
157+ } ) ;
158+ db. insert_block ( & block_info_2) . unwrap ( ) ;
159+
129160 let signer_state_machine = SignerStateMachine {
130161 burn_block,
131162 burn_block_height,
@@ -135,35 +166,43 @@ fn check_capitulate_miner_view() {
135166 } ;
136167
137168 let mut local_state_machine = LocalStateMachine :: Initialized ( signer_state_machine. clone ( ) ) ;
138- assert_eq ! (
139- local_state_machine
140- . capitulate_miner_view( & mut global_eval, & mut db, local_address, & new_update)
141- . unwrap( ) ,
142- current_miner
143- ) ;
144169
145- // Let's set a blocking minority to this different view: evaluator should see no global blocks for the blocking majority and return none
146- // I.e. only if the blocking minority is attempting to reject an reorg should it take priority over the rest.
147- // Let's update 1 other signer to some new miner key (60 percent)
148- for address in addresses. into_iter ( ) . skip ( 1 ) . take ( 1 ) {
170+ // Let's update 40 percent of other signers to some new miner key
171+ for address in addresses. into_iter ( ) . take ( 4 ) {
149172 global_eval. insert_update ( address, new_update. clone ( ) ) ;
150173 }
174+ // Miner view should be None as we can't find consensus on a single miner
151175 assert ! (
152176 local_state_machine
153177 . capitulate_miner_view( & mut global_eval, & mut db, local_address, & new_update)
154178 . is_none( ) ,
155- "Evaluator should have been unable to determine a majority view and return none "
179+ "Evaluator should have told me to capitulate to the old miner "
156180 ) ;
157181
182+ // Mark the old miner's block as globally accepted
158183 db. mark_block_globally_accepted ( & mut block_info_1) . unwrap ( ) ;
159-
160184 db. insert_block ( & block_info_1) . unwrap ( ) ;
161185
162- // Now that the blocking minority references a tenure which would actually get reorged, lets capitulate to their view
186+ // Miner view should stay as the old miner as it has a globally accepted block and 60% consider it valid.
187+ assert_eq ! (
188+ local_state_machine
189+ . capitulate_miner_view( & mut global_eval, & mut db, local_address, & new_update)
190+ . unwrap( ) ,
191+ old_miner,
192+ "Evaluator should have told me to capitulate to the old miner"
193+ ) ;
194+
195+ // Now that we have a globally approved block for the new miner
196+ db. mark_block_globally_accepted ( & mut block_info_2) . unwrap ( ) ;
197+ db. insert_block ( & block_info_2) . unwrap ( ) ;
198+
199+ // Now that the blocking minority references a tenure which would actually get reorged, lets capitulate to the NEW view
200+ // even though both the old and new signer have > 30% approval (it has a higher burn block).
163201 assert_eq ! (
164202 local_state_machine
165203 . capitulate_miner_view( & mut global_eval, & mut db, local_address, & new_update)
166204 . unwrap( ) ,
167- new_miner
205+ new_miner,
206+ "Evaluator should have told me to capitulate to the new miner"
168207 ) ;
169208}
0 commit comments