def setColdStakingEnforcement(self, fEnable=True): new_val = 1563253447 if fEnable else 4070908800 # update spork 17 and mine 1 more block mess = "Enabling" if fEnable else "Disabling" mess += " cold staking with SPORK 17..." self.log.info(mess) res = self.nodes[0].spork("SPORK_17_COLDSTAKING_ENFORCEMENT", new_val) self.log.info(res) assert (res == "success") time.sleep(1) sync_chain(self.nodes)
def run_test(self): self.log.info( "Ensure submitblock can in principle reorg to a competing chain") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 1) (hashY, hashZ) = self.nodes[1].generate(2) assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") (hashA, hashB, hashC) = self.nodes[0].generate(3) assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") (hashE, hashF, hashG) = self.nodes[1].generate(3) assert_equal(self.nodes[1].getblockcount(), 5) assert (hashC != hashG) self.log.info("Connect nodes and check no reorg occurs") # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync) node_sync_via_rpc(self.nodes[0:2]) connect_nodes_bi(self.nodes, 0, 1) assert_equal(self.nodes[0].getbestblockhash(), hashC) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block C again") self.nodes[0].preciousblock(hashC) assert_equal(self.nodes[0].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) sync_chain(self.nodes[0:2] ) # wait because node 1 may not have downloaded hashC assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G again") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node1 prefer block C again") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info( "Mine another block (E-F-G-)H on Node 0 and reorg Node 1") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 6) sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Node1 should not be able to prefer block C anymore") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Mine competing blocks I-J-K-L on Node 2") self.nodes[2].generate(4) assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() self.log.info("Connect nodes and check no reorg occurs") node_sync_via_rpc(self.nodes[1:3]) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) assert_equal(self.nodes[0].getbestblockhash(), hashH) assert_equal(self.nodes[1].getbestblockhash(), hashH) assert_equal(self.nodes[2].getbestblockhash(), hashL) self.log.info("Make Node1 prefer block L") self.nodes[1].preciousblock(hashL) assert_equal(self.nodes[1].getbestblockhash(), hashL) self.log.info("Make Node2 prefer block H") self.nodes[2].preciousblock(hashH) assert_equal(self.nodes[2].getbestblockhash(), hashH)
def run_test(self): self.description = "Performs tests on the Cold Staking P2CS implementation" self.init_test() LAST_POW_BLOCK = 250 NUM_OF_INPUTS = 20 INPUT_VALUE = 50 INITAL_MINED_BLOCKS = LAST_POW_BLOCK + 1 # nodes[0] - coin-owner # nodes[1] - cold-staker # 1) nodes[0] mines 20 blocks. nodes[2] mines 231 blocks. # ----------------------------------------------------------- # Check that SPORK 17 is disabled assert (not self.isColdStakingEnforced()) print("*** 1 ***") self.log.info("Mining %d blocks..." % INITAL_MINED_BLOCKS) self.generateBlock(20, 0) sync_chain(self.nodes) self.log.info("20 Blocks mined.") self.generateBlock(INITAL_MINED_BLOCKS - 20) sync_chain(self.nodes) self.log.info("251 Blocks mined.") # 2) nodes[0] generates a owner address # nodes[1] generates a cold-staking address. # --------------------------------------------- print("*** 2 ***") owner_address = self.nodes[0].getnewaddress() self.log.info("Owner Address: %s" % owner_address) staker_address = self.nodes[1].getnewstakingaddress() self.log.info("Staking Address: %s" % staker_address) # 3) Check enforcement. # --------------------- print("*** 3 ***") self.log.info( "Creating a stake-delegation tx before cold staking enforcement..." ) assert_raises_rpc_error(-4, "The transaction was rejected!", self.nodes[0].delegatestake, staker_address, INPUT_VALUE, owner_address, False, False, True) self.log.info("Good. Cold Staking NOT ACTIVE yet.") # Enable SPORK self.setColdStakingEnforcement() # double check assert (self.isColdStakingEnforced()) # 4) nodes[0] delegates a number of inputs for nodes[1] to stake em. # ------------------------------------------------------------------ print("*** 4 ***") self.log.info("First check warning when using external addresses...") assert_raises_rpc_error( -5, "Only the owner of the key to owneraddress will be allowed to spend these coins", self.nodes[0].delegatestake, staker_address, INPUT_VALUE, "yCgCXC8N5VThhfiaVuKaNLkNnrWduzVnoT") self.log.info("Good. Warning triggered.") self.log.info( "Now force the use of external address creating (but not sending) the delegation..." ) res = self.nodes[0].rawdelegatestake( staker_address, INPUT_VALUE, "yCgCXC8N5VThhfiaVuKaNLkNnrWduzVnoT", True) assert (res is not None and res != "") self.log.info("Good. Warning NOT triggered.") self.log.info("Now delegate with internal owner address..") self.log.info("Try first with a value (0.99) below the threshold") assert_raises_rpc_error(-8, "Invalid amount", self.nodes[0].delegatestake, staker_address, 0.99, owner_address) self.log.info("Nice. it was not possible.") self.log.info( "Then try (creating but not sending) with the threshold value (1.00)" ) res = self.nodes[0].rawdelegatestake(staker_address, 1.00, owner_address) assert (res is not None and res != "") self.log.info("Good. Warning NOT triggered.") self.log.info("Now creating %d real stake-delegation txes..." % NUM_OF_INPUTS) for i in range(NUM_OF_INPUTS): res = self.nodes[0].delegatestake(staker_address, INPUT_VALUE, owner_address) assert (res != None and res["txid"] != None and res["txid"] != "") assert_equal(res["owner_address"], owner_address) assert_equal(res["staker_address"], staker_address) self.generateBlock() sync_chain(self.nodes) self.log.info("%d Txes created." % NUM_OF_INPUTS) # check balances: self.expected_balance = NUM_OF_INPUTS * INPUT_VALUE self.expected_immature_balance = 0 self.checkBalances() # 5) check that the owner (nodes[0]) can spend the coins. # ------------------------------------------------------- print("*** 5 ***") self.log.info("Spending back one of the delegated UTXOs...") delegated_utxos = getDelegatedUtxos(self.nodes[0].listunspent()) assert_equal(20, len(delegated_utxos)) assert_equal(len(delegated_utxos), len(self.nodes[0].listcoldutxos())) u = delegated_utxos[0] txhash = self.spendUTXOwithNode(u, 0) assert (txhash != None) self.log.info("Good. Owner was able to spend - tx: %s" % str(txhash)) self.generateBlock() sync_chain(self.nodes) # check balances after spend. self.expected_balance -= float(u["amount"]) self.checkBalances() self.log.info("Balances check out after spend") assert_equal(19, len(self.nodes[0].listcoldutxos())) # 6) check that the staker CANNOT use the coins to stake yet. # He needs to whitelist the owner first. # ----------------------------------------------------------- print("*** 6 ***") self.log.info( "Trying to generate a cold-stake block before whitelisting the owner..." ) assert_equal(self.nodes[1].getstakingstatus()["mintablecoins"], False) self.log.info( "Nice. Cold staker was NOT able to create the block yet.") self.log.info("Whitelisting the owner...") ret = self.nodes[1].delegatoradd(owner_address) assert (ret) self.log.info("Delegator address %s whitelisted" % owner_address) # 7) check that the staker CANNOT spend the coins. # ------------------------------------------------ print("*** 7 ***") self.log.info( "Trying to spend one of the delegated UTXOs with the cold-staking key..." ) delegated_utxos = getDelegatedUtxos(self.nodes[0].listunspent()) assert_greater_than(len(delegated_utxos), 0) u = delegated_utxos[0] assert_raises_rpc_error( -26, "mandatory-script-verify-flag-failed (Script failed an OP_CHECKCOLDSTAKEVERIFY operation", self.spendUTXOwithNode, u, 1) self.log.info( "Good. Cold staker was NOT able to spend (failed OP_CHECKCOLDSTAKEVERIFY)" ) self.generateBlock() sync_chain(self.nodes) # 8) check that the staker can use the coins to stake a block with internal miner. # -------------------------------------------------------------------------------- print("*** 8 ***") assert_equal(self.nodes[1].getstakingstatus()["mintablecoins"], True) self.log.info("Generating one valid cold-stake block...") self.generateBlock(1, 1) self.log.info("New block created by cold-staking. Trying to submit...") newblockhash = self.nodes[1].getbestblockhash() self.log.info("Block %s submitted" % newblockhash) # Verify that nodes[0] accepts it sync_chain(self.nodes) assert_equal(self.nodes[0].getblockcount(), self.nodes[1].getblockcount()) assert_equal(newblockhash, self.nodes[0].getbestblockhash()) self.log.info("Great. Cold-staked block was accepted!") # check balances after staked block. self.expected_balance -= 50 self.expected_immature_balance += 300 self.checkBalances() self.log.info("Balances check out after staked block") # 9) check that the staker can use the coins to stake a block with a rawtransaction. # ---------------------------------------------------------------------------------- print("*** 9 ***") self.log.info("Generating another valid cold-stake block...") stakeable_coins = getDelegatedUtxos(self.nodes[0].listunspent()) block_n = self.nodes[1].getblockcount() block_hash = self.nodes[1].getblockhash(block_n) prevouts = self.get_prevouts(stakeable_coins, 1) assert_greater_than(len(prevouts), 0) # Create the block new_block = self.create_block(block_hash, prevouts, block_n + 1, 1, staker_address) self.log.info( "New block created (rawtx) by cold-staking. Trying to submit...") # Try to submit the block ret = self.nodes[1].submitblock(bytes_to_hex_str( new_block.serialize())) self.log.info("Block %s submitted." % new_block.hash) assert (ret is None) # Verify that nodes[0] accepts it sync_chain(self.nodes) assert_equal(self.nodes[0].getblockcount(), self.nodes[1].getblockcount()) assert_equal(new_block.hash, self.nodes[0].getbestblockhash()) self.log.info("Great. Cold-staked block was accepted!") # check balances after staked block. self.expected_balance -= 50 self.expected_immature_balance += 300 self.checkBalances() self.log.info("Balances check out after staked block") # 10) check that the staker cannot stake a block changing the coinstake scriptPubkey. # ---------------------------------------------------------------------------------- print("*** 10 ***") self.log.info( "Generating one invalid cold-stake block (changing first coinstake output)..." ) stakeable_coins = getDelegatedUtxos(self.nodes[0].listunspent()) block_n = self.nodes[1].getblockcount() block_hash = self.nodes[1].getblockhash(block_n) prevouts = self.get_prevouts(stakeable_coins, 1) assert_greater_than(len(prevouts), 0) # Create the block new_block = self.create_block(block_hash, prevouts, block_n + 1, 1, staker_address, fInvalid=1) self.log.info( "New block created (rawtx) by cold-staking. Trying to submit...") # Try to submit the block ret = self.nodes[1].submitblock(bytes_to_hex_str( new_block.serialize())) self.log.info("Block %s submitted." % new_block.hash) assert ("rejected" in ret) # Verify that nodes[0] rejects it sync_chain(self.nodes) assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, new_block.hash) self.log.info("Great. Malicious cold-staked block was NOT accepted!") self.checkBalances() self.log.info("Balances check out after (non) staked block") # 11) neither adding different outputs to the coinstake. # ------------------------------------------------------ print("*** 11 ***") self.log.info( "Generating another invalid cold-stake block (adding coinstake output)..." ) stakeable_coins = getDelegatedUtxos(self.nodes[0].listunspent()) block_n = self.nodes[1].getblockcount() block_hash = self.nodes[1].getblockhash(block_n) prevouts = self.get_prevouts(stakeable_coins, 1) assert_greater_than(len(prevouts), 0) # Create the block new_block = self.create_block(block_hash, prevouts, block_n + 1, 1, staker_address, fInvalid=2) self.log.info( "New block created (rawtx) by cold-staking. Trying to submit...") # Try to submit the block ret = self.nodes[1].submitblock(bytes_to_hex_str( new_block.serialize())) self.log.info("Block %s submitted." % new_block.hash) assert_equal(ret, "bad-p2cs-outs") # Verify that nodes[0] rejects it sync_chain(self.nodes) assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, new_block.hash) self.log.info("Great. Malicious cold-staked block was NOT accepted!") self.checkBalances() self.log.info("Balances check out after (non) staked block") # 12) Now node[0] gets mad and spends all the delegated coins, voiding the P2CS contracts. # ---------------------------------------------------------------------------------------- self.log.info("Let's void the contracts.") self.generateBlock() sync_chain(self.nodes) print("*** 12 ***") self.log.info( "Cancel the stake delegation spending the cold stakes...") delegated_utxos = getDelegatedUtxos(self.nodes[0].listunspent()) # remove one utxo to spend later final_spend = delegated_utxos.pop() txhash = self.spendUTXOsWithNode(delegated_utxos, 0) assert (txhash != None) self.log.info( "Good. Owner was able to void the stake delegations - tx: %s" % str(txhash)) self.generateBlock() sync_chain(self.nodes) # deactivate SPORK 17 and check that the owner can still spend the last utxo self.setColdStakingEnforcement(False) assert (not self.isColdStakingEnforced()) txhash = self.spendUTXOsWithNode([final_spend], 0) assert (txhash != None) self.log.info( "Good. Owner was able to void the last stake delegation (with SPORK 17 disabled) - tx: %s" % str(txhash)) self.generateBlock() sync_chain(self.nodes) # check balances after big spend. self.expected_balance = 0 self.checkBalances() self.log.info( "Balances check out after the delegations have been voided.") # re-activate SPORK17 self.setColdStakingEnforcement() assert (self.isColdStakingEnforced()) # 13) check that coinstaker is empty and can no longer stake. # ----------------------------------------------------------- print("*** 13 ***") self.log.info("Trying to generate one cold-stake block again...") assert_equal(self.nodes[1].getstakingstatus()["mintablecoins"], False) self.log.info( "Cigar. Cold staker was NOT able to create any more blocks.") # 14) check balances when mature. # ----------------------------------------------------------- print("*** 14 ***") self.log.info("Staking 100 blocks to mature last 2...") self.generateBlock(100) self.expected_balance = self.expected_immature_balance self.expected_immature_balance = 0 self.checkBalances() self.log.info("Balances check out after maturation.\n")
def run_test(self): self.log.info( "Ensure submitblock can in principle reorg to a competing chain") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 1) (hashY, hashZ) = self.nodes[1].generate(2) assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") (hashA, hashB, hashC) = self.nodes[0].generate(3) assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") (hashE, hashF, hashG) = self.nodes[1].generate(3) assert_equal(self.nodes[1].getblockcount(), 5) assert(hashC != hashG) self.log.info("Connect nodes and check no reorg occurs") # Submit competing blocks via RPC so any reorg should occur before we # proceed (no way to wait on inaction for p2p sync) node_sync_via_rpc(self.nodes[0:2]) connect_nodes_bi(self.nodes, 0, 1) assert_equal(self.nodes[0].getbestblockhash(), hashC) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block C again") self.nodes[0].preciousblock(hashC) assert_equal(self.nodes[0].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G again") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node1 prefer block C again") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info( "Mine another block (E-F-G-)H on Node 0 and reorg Node 1") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 6) sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Node1 should not be able to prefer block C anymore") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Mine competing blocks I-J-K-L on Node 2") self.nodes[2].generate(4) assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() self.log.info("Connect nodes and check no reorg occurs") node_sync_via_rpc(self.nodes[1:3]) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) assert_equal(self.nodes[0].getbestblockhash(), hashH) assert_equal(self.nodes[1].getbestblockhash(), hashH) assert_equal(self.nodes[2].getbestblockhash(), hashL) self.log.info("Make Node1 prefer block L") self.nodes[1].preciousblock(hashL) assert_equal(self.nodes[1].getbestblockhash(), hashL) self.log.info("Make Node2 prefer block H") self.nodes[2].preciousblock(hashH) assert_equal(self.nodes[2].getbestblockhash(), hashH)
def run_test(self): self.setup_stake_coins(self.nodes[0], self.nodes[1], self.nodes[2]) for i in range(self.num_nodes): self.nodes[i].add_p2p_connection(P2PInterface()) network_thread_start() wait_until(lambda: all(self.nodes[i].p2p.got_verack() for i in range(self.num_nodes)), timeout=10) self.log.info( "Ensure submitblock can in principle reorg to a competing chain") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 1) hashZ = self.nodes[1].generate(2)[-1] assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") hashC = self.nodes[0].generate(3)[-1] assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") hashG = self.nodes[1].generate(3)[-1] assert_equal(self.nodes[1].getblockcount(), 5) assert hashC != hashG self.log.info("Connect nodes and check no reorg occurs") # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync) node_sync_via_rpc(self.nodes[0:2]) connect_nodes_bi(self.nodes, 0, 1) assert_equal(self.nodes[0].getbestblockhash(), hashC) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block C again") self.nodes[0].preciousblock(hashC) assert_equal(self.nodes[0].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) sync_chain(self.nodes[0:2] ) # wait because node 1 may not have downloaded hashC assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G again") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node1 prefer block C again") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info( "Mine another block (E-F-G-)H on Node 0 and reorg Node 1") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 6) sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Node1 should not be able to prefer block C anymore") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Mine competing blocks I-J-K-L on Node 2") self.nodes[2].generate(4) assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() self.log.info("Connect nodes and check no reorg occurs") node_sync_via_rpc(self.nodes[1:3]) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) assert_equal(self.nodes[0].getbestblockhash(), hashH) assert_equal(self.nodes[1].getbestblockhash(), hashH) assert_equal(self.nodes[2].getbestblockhash(), hashL) self.log.info("Make Node1 prefer block L") self.nodes[1].preciousblock(hashL) assert_equal(self.nodes[1].getbestblockhash(), hashL) self.log.info("Make Node2 prefer block H") self.nodes[2].preciousblock(hashH) assert_equal(self.nodes[2].getbestblockhash(), hashH)
def run_test(self): self.log.info( "Ensure submitblock can in principle reorg to a competing chain") gen_address = lambda i: self.nodes[i].get_deterministic_priv_key( ).address # A non-wallet address to mine to self.nodes[0].generatetoaddress(1, gen_address(0)) assert_equal(self.nodes[0].getblockcount(), 1) hashZ = self.nodes[1].generatetoaddress(2, gen_address(1))[-1] assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") hashC = self.nodes[0].generatetoaddress(3, gen_address(0))[-1] assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") hashG = self.nodes[1].generatetoaddress(3, gen_address(1))[-1] assert_equal(self.nodes[1].getblockcount(), 5) assert (hashC != hashG) self.log.info("Connect nodes and check no reorg occurs") # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync) node_sync_via_rpc(self.nodes[0:2]) connect_nodes_bi(self.nodes, 0, 1) assert_equal(self.nodes[0].getbestblockhash(), hashC) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block C again") self.nodes[0].preciousblock(hashC) assert_equal(self.nodes[0].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) sync_chain(self.nodes[0:2] ) # wait because node 1 may not have downloaded hashC assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G again") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node1 prefer block C again") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info( "Mine another block (E-F-G-)H on Node 0 and reorg Node 1") self.nodes[0].generatetoaddress(1, gen_address(0)) assert_equal(self.nodes[0].getblockcount(), 6) sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Node1 should not be able to prefer block C anymore") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Mine competing blocks I-J-K-L on Node 2") self.nodes[2].generatetoaddress(4, gen_address(2)) assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() self.log.info("Connect nodes and check no reorg occurs") node_sync_via_rpc(self.nodes[1:3]) connect_nodes_bi(self.nodes, 1, 2) connect_nodes_bi(self.nodes, 0, 2) assert_equal(self.nodes[0].getbestblockhash(), hashH) assert_equal(self.nodes[1].getbestblockhash(), hashH) assert_equal(self.nodes[2].getbestblockhash(), hashL) self.log.info("Make Node1 prefer block L") self.nodes[1].preciousblock(hashL) assert_equal(self.nodes[1].getbestblockhash(), hashL) self.log.info("Make Node2 prefer block H") self.nodes[2].preciousblock(hashH) assert_equal(self.nodes[2].getbestblockhash(), hashH)
def run_test(self): p0, p1, p2, v0 = self.nodes self.setup_stake_coins(p0, p1, p2, v0) # Leave IBD self.generate_sync(p0) self.log.info("Setup deposit") setup_deposit(self, p0, [v0]) sync_blocks([p0, p1, p2, v0]) self.log.info("Setup test prerequisites") # get to up to block 49, just one before the new checkpoint for _ in range(18): generate_block(p0) assert_equal(p0.getblockcount(), 49) sync_blocks([p0, p1, p2, v0]) assert_finalizationstate(p0, {'currentEpoch': 5, 'lastJustifiedEpoch': 4, 'lastFinalizedEpoch': 3}) # disconnect p0 # v0: p1, p2 # p0: # p1: v0 # p2: v0 disconnect_nodes(p0, v0.index) disconnect_nodes(p0, p1.index) # disconnect p2 # v0: p1 # p0: # p1: v0 # p2: disconnect_nodes(p2, v0.index) # disconnect p1 # v0: # p0: # p1: # p2: disconnect_nodes(p1, v0.index) # generate long chain in p0 but don't justify it # F J # 30 .. 40 .. 89 -- p0 for _ in range(40): generate_block(p0) assert_equal(p0.getblockcount(), 89) assert_finalizationstate(p0, {'currentEpoch': 9, 'lastJustifiedEpoch': 4, 'lastFinalizedEpoch': 3}) # generate short chain in p1 and justify it # on the 6th and 7th epochs sync with validator # F J # 30 .. 40 .. 49 .. .. .. .. .. .. 89 -- p0 # \ # 50 .. 60 .. 69 -- p1 # F J # get to the 6th epoch p1.generatetoaddress(2, p1.getnewaddress('', 'bech32')) self.wait_for_vote_and_disconnect(finalizer=v0, node=p1) # get to the 7th epoch p1.generatetoaddress(10, p1.getnewaddress('', 'bech32')) self.wait_for_vote_and_disconnect(finalizer=v0, node=p1) # generate the rest of the blocks p1.generatetoaddress(8, p1.getnewaddress('', 'bech32')) connect_nodes(p1, v0.index) sync_blocks([p1, v0]) assert_equal(p1.getblockcount(), 69) assert_finalizationstate(p1, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, 'lastFinalizedEpoch': 5}) # connect p2 with p0 and p1; p2 must switch to the longest justified p1 # v0: p1 # p0: p2 # p1: v0, p2 # p2: p0, p1 self.log.info("Test fresh node sync") connect_nodes(p2, p0.index) connect_nodes(p2, p1.index) sync_chain([p1, p2]) assert_equal(p1.getblockcount(), 69) assert_equal(p2.getblockcount(), 69) assert_finalizationstate(p1, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, 'lastFinalizedEpoch': 5}) assert_finalizationstate(p2, {'currentEpoch': 7, 'lastJustifiedEpoch': 6, 'lastFinalizedEpoch': 5}) # connect p0 with p1, p0 must disconnect its longest but not justified fork and choose p1 # v0: p1 # p0: p1, p2 # p1: v0, p0, p2 # p2: p0, p1 self.log.info("Test longest node reverts to justified") connect_nodes(p0, p1.index) sync_chain([p0, p1]) # check if p0 accepted shortest in terms of blocks but longest justified chain assert_equal(p0.getblockcount(), 69) assert_equal(p1.getblockcount(), 69) assert_equal(v0.getblockcount(), 69) # generate more blocks to make sure they're processed self.log.info("Test all nodes continue to work as usual") for _ in range(30): generate_block(p0) sync_chain([p0, p1, p2, v0]) assert_equal(p0.getblockcount(), 99) for _ in range(30): generate_block(p1) sync_chain([p0, p1, p2, v0]) assert_equal(p1.getblockcount(), 129) for _ in range(30): generate_block(p2) sync_chain([p0, p1, p2, v0]) assert_equal(p2.getblockcount(), 159) # disconnect all nodes # v0: # p0: # p1: # p2: self.log.info("Test nodes sync after reconnection") disconnect_nodes(v0, p1.index) disconnect_nodes(p0, p1.index) disconnect_nodes(p0, p2.index) disconnect_nodes(p1, p2.index) for _ in range(10): generate_block(p0) for _ in range(20): generate_block(p1) for _ in range(30): generate_block(p2) assert_equal(p0.getblockcount(), 169) assert_equal(p1.getblockcount(), 179) assert_equal(p2.getblockcount(), 189) # connect validator back to p1 # v0: p1 # p0: p1 # p1: v0, p0, p2 # p2: p1 connect_nodes(p1, v0.index) sync_blocks([p1, v0]) connect_nodes(p1, p0.index) connect_nodes(p1, p2.index) sync_chain([p0, p1, p2, v0])
def test_justification_over_chain_work(self): """ Test that justification has priority over chain work """ def seen_block(node, blockhash): try: node.getblock(blockhash) return True except JSONRPCException: return False def connect_sync_disconnect(node1, node2, blockhash): connect_nodes(node1, node2.index) wait_until(lambda: seen_block(node1, blockhash), timeout=10) wait_until(lambda: node1.getblockcount() == node2.getblockcount(), timeout=5) assert_equal(node1.getblockhash(node1.getblockcount()), blockhash) disconnect_nodes(node1, node2.index) node0 = self.nodes[0] node1 = self.nodes[1] node2 = self.nodes[2] validator = self.nodes[3] self.setup_stake_coins(node0, node1, node2, validator) connect_nodes(node0, node1.index) connect_nodes(node0, node2.index) connect_nodes(node0, validator.index) # leave IBD node0.generatetoaddress(1, node0.getnewaddress('', 'bech32')) sync_blocks([node0, node1, node2, validator], timeout=10) payto = validator.getnewaddress('', 'legacy') txid = validator.deposit(payto, 1500) wait_until(lambda: self.have_tx_in_mempool([node0, node1, node2], txid), timeout=10) disconnect_nodes(node0, node1.index) disconnect_nodes(node0, node2.index) disconnect_nodes(node0, validator.index) assert_equal(len(node0.getpeerinfo()), 0) # F F F F J # e0 - e1 - e2 - e3 - e4 - e5 - e6[26] node0.generatetoaddress(25, node0.getnewaddress('', 'bech32')) assert_equal(node0.getblockcount(), 26) assert_finalizationstate(node0, {'currentDynasty': 3, 'currentEpoch': 6, 'lastJustifiedEpoch': 4, 'lastFinalizedEpoch': 3, 'validators': 1}) connect_nodes(node0, node1.index) connect_nodes(node0, node2.index) sync_blocks([node0, node1, node2]) disconnect_nodes(node0, node1.index) disconnect_nodes(node0, node2.index) # generate fork with no commits. node0 must switch to it # 26 node1 # \ # - b27 node0, node2 b27 = node2.generatetoaddress(1, node2.getnewaddress('', 'bech32'))[-1] connect_sync_disconnect(node0, node2, b27) assert_equal(node0.getblockcount(), 27) # generate fork with justified commits. node0 must switch to it # - 27 - b28 node0, node1 # / # 26 # \ # - b27 node2 self.wait_for_vote_and_disconnect(finalizer=validator, node=node1) b28 = node1.generatetoaddress(2, node1.getnewaddress('', 'bech32'))[-1] connect_sync_disconnect(node0, node1, b28) assert_equal(node0.getblockcount(), 28) assert_finalizationstate(node0, {'currentDynasty': 3, 'currentEpoch': 6, 'lastJustifiedEpoch': 5, 'lastFinalizedEpoch': 4, 'validators': 1}) self.log.info('node successfully switched to longest justified fork') # generate longer but not justified fork. node0 shouldn't switch # - 27 - b28 node0, node1, node2 # / # 26 # \ # - 27 - 28 - 29 - b30 b30 = node2.generatetoaddress(3, node2.getnewaddress('', 'bech32'))[-1] assert_equal(node2.getblockcount(), 30) assert_equal(node0.getblockcount(), 28) connect_nodes(node0, node2.index) sync_chain([node0, node2], timeout=10) sync_blocks([node0, node2], timeout=10) assert_equal(node0.getblockcount(), 28) assert_equal(node0.getblockhash(28), b28) assert_equal(node0.getfinalizationstate()['lastJustifiedEpoch'], 5) self.log.info('node did not switch to heaviest but less justified fork') assert_equal(node2.getblockcount(), 28) assert_equal(node2.getblockhash(28), b28) assert_equal(node2.getfinalizationstate()['lastJustifiedEpoch'], 5) self.log.info('node switched to longest justified fork with less work') self.stop_node(node0.index) self.stop_node(node1.index) self.stop_node(node2.index) self.stop_node(validator.index)