def run_test(self): self.disable_mocktime() # Additional connections to miner and owner for nodePos in [self.minerPos, self.controllerPos]: self.connect_to_all(nodePos) miner = self.nodes[self.minerPos] controller = self.nodes[self.controllerPos] dummy_add = controller.getnewaddress("dummy") # Enforce mn payments and reject legacy mns at block 131 self.activate_spork(0, "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT") assert_equal("success", self.set_spork(self.minerPos, "SPORK_21_LEGACY_MNS_MAX_HEIGHT", 130)) time.sleep(1) assert_equal([130] * self.num_nodes, [self.get_spork(x, "SPORK_21_LEGACY_MNS_MAX_HEIGHT") for x in range(self.num_nodes)]) mns = [] # Mine 100 blocks self.log.info("Mining...") miner.generate(110) self.sync_blocks() self.assert_equal_for_all(110, "getblockcount") # Test rejection before enforcement self.log.info("Testing rejection of ProRegTx before DIP3 activation...") assert_raises_rpc_error(-1, "Evo upgrade is not active yet", self.add_new_dmn, mns, "internal") assert_raises_rpc_error(-1, "Evo upgrade is not active yet", self.add_new_dmn, mns, "fund") # Can create the raw proReg dmn = create_new_dmn(2, controller, dummy_add, None) tx, sig = self.protx_register_ext(miner, controller, dmn, None, False) # but cannot send it assert_raises_rpc_error(-1, "Evo upgrade is not active yet", miner.protx_register_submit, tx, sig) self.log.info("Done. Now mine blocks till enforcement...") # Check that no coin has been locked by the controller yet assert_equal(len(controller.listlockunspent()), 0) # DIP3 activates at block 130. miner.generate(130 - miner.getblockcount()) self.sync_blocks() self.assert_equal_for_all(130, "getblockcount") # -- DIP3 enforced and SPORK_21 active here -- self.wait_until_mnsync_completed() # enabled/total masternodes: 0/0 self.check_mn_enabled_count(0, 0) # Create 3 DMNs and init the remote nodes self.log.info("Initializing masternodes...") self.add_new_dmn(mns, "internal") self.add_new_dmn(mns, "external") self.add_new_dmn(mns, "fund") for mn in mns: self.nodes[mn.idx].initmasternode(mn.operator_sk) time.sleep(1) miner.generate(1) self.sync_blocks() # enabled/total masternodes: 3/3 self.check_mn_enabled_count(3, 3) # Init the other 3 remote nodes before creating the ProReg tx self.log.info("Initializing more masternodes...") op_keys = [] for i in range(3): idx = 2 + len(mns) + i bls_keypair = controller.generateblskeypair() self.nodes[idx].initmasternode(bls_keypair["secret"]) op_keys.append([bls_keypair["public"], bls_keypair["secret"]]) time.sleep(1) # Now send the ProReg txes and check list self.add_new_dmn(mns, "internal", op_keys[0]) self.add_new_dmn(mns, "external", op_keys[1]) self.add_new_dmn(mns, "fund", op_keys[2]) miner.generate(2) self.sync_blocks() time.sleep(1) self.log.info("Masternodes started.") # enabled/total masternodes: 6/6 self.check_mn_enabled_count(6, 6) self.check_mn_list(mns) # Check status from remote nodes assert_equal([self.nodes[idx].getmasternodestatus()['status'] for idx in range(2, self.num_nodes)], ["Ready"] * (self.num_nodes - 2)) self.log.info("All masternodes ready.") # Restart the controller and check that the collaterals are still locked self.log.info("Restarting controller...") self.restart_controller() time.sleep(1) for mn in mns: if not is_coin_locked_by(controller, mn.collateral): raise Exception( "Collateral %s of mn with idx=%d is not locked" % (mn.collateral, mn.idx) ) self.log.info("Collaterals still locked.") # Test collateral spending dmn = mns.pop(randrange(len(mns))) # pop one at random self.log.info("Spending collateral of mn with idx=%d..." % dmn.idx) spend_txid = spend_mn_collateral(controller, dmn) self.sync_mempools([miner, controller]) miner.generate(1) self.sync_blocks() assert_greater_than(miner.getrawtransaction(spend_txid, True)["confirmations"], 0) # enabled/total masternodes: 5/5 self.check_mn_enabled_count(5, 5) self.check_mn_list(mns) # Register dmn again, with the collateral of dmn2 # dmn must be added again to the list, and dmn2 must be removed dmn2 = mns.pop(randrange(len(mns))) # pop one at random dmn_keys = [dmn.operator_pk, dmn.operator_sk] dmn2_keys = [dmn2.operator_pk, dmn2.operator_sk] self.log.info("Reactivating node %d reusing the collateral of node %d..." % (dmn.idx, dmn2.idx)) mns.append(self.register_new_dmn(dmn.idx, self.minerPos, self.controllerPos, "external", outpoint=dmn2.collateral, op_blskeys=dmn_keys)) miner.generate(1) self.sync_blocks() # enabled/total masternodes: 5/5 self.check_mn_enabled_count(5, 5) self.check_mn_list(mns) # Now try to register dmn2 again with an already-used IP self.log.info("Trying duplicate IP...") rand_idx = mns[randrange(len(mns))].idx assert_raises_rpc_error(-1, "bad-protx-dup-IP-address", self.register_new_dmn, rand_idx, self.minerPos, self.controllerPos, "fund", op_blskeys=dmn2_keys) # Now try with duplicate operator key self.log.info("Trying duplicate operator key...") dmn2b = create_new_dmn(dmn2.idx, controller, dummy_add, dmn_keys) assert_raises_rpc_error(-1, "bad-protx-dup-operator-key", self.protx_register_fund, miner, controller, dmn2b, dummy_add) # Now try with duplicate owner key self.log.info("Trying duplicate owner key...") dmn2c = create_new_dmn(dmn2.idx, controller, dummy_add, dmn2_keys) dmn2c.owner = mns[randrange(len(mns))].owner assert_raises_rpc_error(-1, "bad-protx-dup-owner-key", self.protx_register_fund, miner, controller, dmn2c, dummy_add) # Finally, register it properly. This time setting 10% of the reward for the operator op_rew = {"reward": 10.00, "address": self.nodes[dmn2.idx].getnewaddress()} self.log.info("Reactivating the node with a new registration (with operator reward)...") dmn2c = create_new_dmn(dmn2.idx, controller, dummy_add, dmn2_keys) self.protx_register_fund(miner, controller, dmn2c, dummy_add, op_rew) mns.append(dmn2c) time.sleep(1) self.sync_mempools([miner, controller]) miner.generate(6) self.sync_blocks() json_tx = self.nodes[dmn2c.idx].getrawtransaction(dmn2c.proTx, True) assert_greater_than(json_tx['confirmations'], 0) self.check_proreg_payload(dmn2c, json_tx) # enabled/total masternodes: 6/6 self.check_mn_enabled_count(6, 6) self.check_mn_list(mns) # 6 masternodes again # Test payments. # Mine 12 blocks and check that each masternode has been paid exactly twice. # Save last paid masternode. Check that it's the last paid also after the 12 blocks. # Note: dmn2 sends (2 * 0.3 PIV) to the operator, and (2 * 2.7 PIV) to the owner self.log.info("Testing masternode payments...") last_paid_mn = self.get_last_paid_mn() starting_balances = {"operator": self.get_addr_balance(self.nodes[dmn2c.idx], op_rew["address"])} for mn in mns: starting_balances[mn.payee] = self.get_addr_balance(controller, mn.payee) miner.generate(12) self.sync_blocks() for mn in mns: bal = self.get_addr_balance(controller, mn.payee) expected = starting_balances[mn.payee] + (Decimal('6.0') if mn.idx != dmn2c.idx else Decimal('5.4')) if bal != expected: raise Exception("Invalid balance (%s != %s) for node %d" % (bal, expected, mn.idx)) self.log.info("All masternodes paid twice.") assert_equal(self.get_addr_balance(self.nodes[dmn2c.idx], op_rew["address"]), starting_balances["operator"] + Decimal('0.6')) self.log.info("Operator paid twice.") assert_equal(last_paid_mn, self.get_last_paid_mn()) self.log.info("Order preserved.") # Test invalid payment self.wait_until_mnsync_completed() # just to be sure self.log.info("Testing invalid masternode payment...") mn_payee_script = miner.validateaddress(miner.getnewaddress())['scriptPubKey'] block = self.create_block(mn_payee_script, miner.getblock(miner.getbestblockhash(), True)) block.solve() assert_equal(miner.submitblock(bytes_to_hex_str(block.serialize())), "bad-cb-payee") # Test ProUpServ txes self.log.info("Trying to update a non-existent masternode...") assert_raises_rpc_error(-8, "not found", miner.protx_update_service, "%064x" % getrandbits(256), "127.0.0.1:1000") self.log.info("Trying to update an IP address to an already used one...") assert_raises_rpc_error(-1, "bad-protx-dup-addr", miner.protx_update_service, mns[0].proTx, mns[1].ipport, "", mns[0].operator_sk) self.log.info("Trying to update the payout address when the reward is 0...") assert_raises_rpc_error(-8, "Operator reward is 0. Cannot set operator payout address", miner.protx_update_service, mns[0].proTx, "", miner.getnewaddress(), mns[0].operator_sk) self.log.info("Trying to update the operator payee to an invalid address...") assert_raises_rpc_error(-5, "invalid PIVX address InvalidPayee", miner.protx_update_service, dmn2c.proTx, "", "InvalidPayee", "") self.log.info("Update IP address...") mns[0].ipport = "127.0.0.1:1000" # Do it from the remote node (so no need to pass the operator BLS secret key) remote_node = self.nodes[mns[0].idx] # Send first some funds miner.sendtoaddress(remote_node.getnewaddress(), 1.0) miner.generate(1) self.sync_blocks() # Then send the ProUpServ tx from the masternode remote_node.protx_update_service(mns[0].proTx, mns[0].ipport) self.sync_mempools([miner, remote_node]) miner.generate(1) self.sync_blocks() self.check_mn_list(mns) self.log.info("Update operator payout address...") # This time send the ProUpServ tx directly from the miner, giving the operator BLS secret key new_address = self.nodes[dmn2c.idx].getnewaddress() miner.protx_update_service(dmn2c.proTx, dmn2c.ipport, new_address, dmn2c.operator_sk) miner.generate(len(mns) + 1) self.sync_blocks() # Check payment to new address self.log.info("Checking payment...") assert_equal(self.get_addr_balance(self.nodes[dmn2c.idx], new_address), Decimal('0.3')) # Test ProUpReg txes self.log.info("Trying to update a non-existent masternode...") assert_raises_rpc_error(-8, "not found", miner.protx_update_registrar, "%064x" % getrandbits(256), "", "", "") self.log.info("Trying to update an operator address to an already used one...") assert_raises_rpc_error(-1, "bad-protx-dup-key", controller.protx_update_registrar, mns[0].proTx, mns[1].operator_pk, "", "") self.log.info("Trying to update the payee to an invalid address...") assert_raises_rpc_error(-5, "invalid PIVX address InvalidPayee", controller.protx_update_registrar, mns[0].proTx, "", "", "InvalidPayee") self.log.info("Update operator keys...") bls_keypair = self.nodes[mns[0].idx].generateblskeypair() mns[0].operator_pk = bls_keypair["public"] mns[0].operator_sk = bls_keypair["secret"] # Controller should already have the key (as it was generated there), no need to pass it controller.protx_update_registrar(mns[0].proTx, mns[0].operator_pk, "", "") self.sync_mempools([miner, controller]) miner.generate(1) self.sync_blocks() # enabled/total masternodes: 5/6 # Updating the operator key, clears the IP (and puts the mn in PoSe banned state) self.check_mn_enabled_count(5, 6) mns[0].ipport = "[::]:0" self.check_mn_list(mns) old_mn0_balance = self.get_addr_balance(controller, mns[0].payee) self.log.info("Update operator address (with external key)...") bls_keypair = self.nodes[mns[0].idx].generateblskeypair() mns[0].operator_pk = bls_keypair["public"] mns[0].operator_sk = bls_keypair["secret"] ownerKey = controller.dumpprivkey(mns[0].owner) miner.protx_update_registrar(mns[0].proTx, mns[0].operator_pk, "", "", ownerKey) miner.generate(1) self.sync_blocks() self.check_mn_enabled_count(5, 6) # stil not valid until new operator sends proUpServ self.check_mn_list(mns) self.log.info("Update voting address...") mns[1].voting = controller.getnewaddress() controller.protx_update_registrar(mns[1].proTx, "", mns[1].voting, "") self.sync_mempools([miner, controller]) miner.generate(1) self.sync_blocks() self.check_mn_enabled_count(5, 6) self.check_mn_list(mns) self.log.info("Update payout address...") old_payee = mns[2].payee mns[2].payee = controller.getnewaddress() controller.protx_update_registrar(mns[2].proTx, "", "", mns[2].payee) self.sync_mempools([miner, controller]) miner.generate(1) self.sync_blocks() old_mn2_bal = self.get_addr_balance(controller, old_payee) miner.generate(len(mns)-1) self.sync_blocks() self.check_mn_enabled_count(5, 6) self.check_mn_list(mns) # Check payment to new address self.log.info("Checking payments...") assert_equal(self.get_addr_balance(controller, old_payee), old_mn2_bal) assert_equal(self.get_addr_balance(controller, mns[2].payee), Decimal('3')) # The PoSe banned node didn't receive any more payment assert_equal(self.get_addr_balance(controller, mns[0].payee), old_mn0_balance) # Test ProUpRev txes self.log.info("Trying to revoke a non-existent masternode...") assert_raises_rpc_error(-8, "not found", miner.protx_revoke, "%064x" % getrandbits(256)) self.log.info("Trying to revoke with invalid reason...") assert_raises_rpc_error(-8, "invalid reason", controller.protx_revoke, mns[3].proTx, mns[3].operator_sk, 100) self.log.info("Revoke masternode...") # Do it from the remote node (so no need to pass the operator BLS secret key) remote_node = self.nodes[mns[3].idx] # Send first some funds miner.sendtoaddress(remote_node.getnewaddress(), 1.0) miner.generate(1) self.sync_blocks() # Then send the ProUpRev tx from the masternode remote_node.protx_revoke(mns[3].proTx, "", 1) mns[3].revoked() self.sync_mempools([miner, remote_node]) miner.generate(1) self.sync_blocks() self.check_mn_enabled_count(4, 6) # mn3 has been revoked self.check_mn_list(mns) old_mn3_bal = self.get_addr_balance(controller, mns[3].payee) # This time send the ProUpRev tx directly from the miner, giving the operator BLS secret key self.log.info("Revoke masternode (with external key)...") miner.protx_revoke(mns[4].proTx, mns[4].operator_sk, 2) mns[4].revoked() miner.generate(1) self.sync_blocks() self.check_mn_list(mns) old_mn4_bal = self.get_addr_balance(controller, mns[4].payee) miner.generate(len(mns) + 1) self.sync_blocks() # enabled/total masternodes: 3/6 (mn0 banned, mn3 and mn4 revoked) self.check_mn_enabled_count(3, 6) self.check_mn_list(mns) # Check (no) payments self.log.info("Checking payments...") assert_equal(self.get_addr_balance(controller, mns[3].payee), old_mn3_bal) assert_equal(self.get_addr_balance(controller, mns[4].payee), old_mn4_bal) # Test reviving a masternode self.log.info("Reviving a masternode...") bls_keypair = controller.generateblskeypair() mns[3].operator_pk = bls_keypair["public"] mns[3].operator_sk = bls_keypair["secret"] miner.protx_update_registrar(mns[3].proTx, mns[3].operator_pk, "", "", controller.dumpprivkey(mns[3].owner)) miner.generate(1) mns[3].ipport = "127.0.0.1:3000" miner.protx_update_service(mns[3].proTx, mns[3].ipport, "", mns[3].operator_sk) miner.generate(len(mns)) self.sync_blocks() # enabled/total masternodes: 4/6 (mn3 is back) self.check_mn_enabled_count(4, 6) self.check_mn_list(mns) self.log.info("Checking payments...") assert_equal(self.get_addr_balance(controller, mns[3].payee), old_mn3_bal + Decimal('3')) self.log.info("All good.")
def run_test(self): self.disable_mocktime() nodeA = self.nodes[0] nodeB = self.nodes[1] free_idx = 1 # unique id for masternodes. first available. # Enforce mn payments and reject legacy mns at block 202 self.activate_spork(0, "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT") assert_equal("success", self.set_spork(0, "SPORK_21_LEGACY_MNS_MAX_HEIGHT", 201)) time.sleep(1) assert_equal([201] * self.num_nodes, [ self.get_spork(x, "SPORK_21_LEGACY_MNS_MAX_HEIGHT") for x in range(self.num_nodes) ]) # Mine 201 blocks self.log.info("Mining...") nodeA.generate(25) self.sync_blocks() nodeB.generate(25) self.sync_blocks() nodeA.generate(50) self.sync_blocks() nodeB.generate(101) self.sync_blocks() self.assert_equal_for_all(201, "getblockcount") # Register two masternodes before the split collateral_addr = nodeA.getnewaddress( ) # for both collateral and payouts pre_split_mn1 = create_new_dmn(100, nodeA, nodeA.getnewaddress(), None) pre_split_mn2 = create_new_dmn(200, nodeA, nodeA.getnewaddress(), None) self.register_masternode(nodeA, pre_split_mn1, collateral_addr) self.register_masternode(nodeA, pre_split_mn2, collateral_addr) nodeA.generate(1) self.sync_blocks() mnsA = [pre_split_mn1, pre_split_mn2] mnsB = [pre_split_mn1, pre_split_mn2] self.check_mn_list_on_node(0, mnsA) self.check_mn_list_on_node(1, mnsB) self.log.info("Pre-split masternodes registered.") # Disconnect the nodes self.disconnect_all() # network splits at block 203 # # -- CHAIN A -- # # Register 5 masternodes, then mine 5 blocks self.log.info("Registering masternodes on chain A...") for _ in range(5): dmn = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 self.register_masternode(nodeA, dmn, collateral_addr) mnsA.append(dmn) nodeA.generate(5) self.check_mn_list_on_node(0, mnsA) self.log.info("Masternodes registered on chain A.") # Now create a collateral (which will be used later to register a masternode) funding_txid = nodeA.sendtoaddress(collateral_addr, Decimal('100')) nodeA.generate(1) funding_tx_json = nodeA.getrawtransaction(funding_txid, True) assert_greater_than(funding_tx_json["confirmations"], 0) initial_collateral = COutPoint(int(funding_txid, 16), get_collateral_vout(funding_tx_json)) # Lock any utxo with less than 106 confs (e.g. change), so we can resurrect everything for x in nodeA.listunspent(0, 106): nodeA.lockunspent(False, [{"txid": x["txid"], "vout": x["vout"]}]) # Now send a valid proReg tx to the mempool, without mining it mempool_dmn1 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 self.register_masternode(nodeA, mempool_dmn1, collateral_addr) assert mempool_dmn1.proTx in nodeA.getrawmempool() # Try sending a proReg tx with same owner self.log.info("Testing in-mempool duplicate-owner rejection...") dmn_A1 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 dmn_A1.owner = mempool_dmn1.owner assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A1, collateral_addr) assert dmn_A1.proTx not in nodeA.getrawmempool() # Try sending a proReg tx with same operator self.log.info("Testing in-mempool duplicate-operator rejection...") dmn_A2 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 dmn_A2.operator_pk = mempool_dmn1.operator_pk assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A2, collateral_addr) assert dmn_A2.proTx not in nodeA.getrawmempool() # Try sending a proReg tx with same IP self.log.info("Testing proReg in-mempool duplicate-IP rejection...") dmn_A3 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 dmn_A3.ipport = mempool_dmn1.ipport assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A3, collateral_addr) assert dmn_A3.proTx not in nodeA.getrawmempool() # Now send other 2 valid proReg tx to the mempool, without mining them self.log.info("Sending more ProReg txes to the mempool...") mempool_dmn2 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 mempool_dmn3 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 self.register_masternode(nodeA, mempool_dmn2, collateral_addr) self.register_masternode(nodeA, mempool_dmn3, collateral_addr) # Send to the mempool a ProRegTx using the collateral mined after the split mempool_dmn4 = create_new_dmn(free_idx, nodeA, collateral_addr, None) mempool_dmn4.collateral = initial_collateral self.protx_register_ext(nodeA, nodeA, mempool_dmn4, mempool_dmn4.collateral, True) # Now send a valid proUpServ tx to the mempool, without mining it proupserv1_txid = nodeA.protx_update_service(pre_split_mn1.proTx, "127.0.0.1:1000", "", pre_split_mn1.operator_sk) # Try sending another update, reusing the same ip of the previous mempool tx self.log.info("Testing proUpServ in-mempool duplicate-IP rejection...") assert_raises_rpc_error(-26, "protx-dup", nodeA.protx_update_service, mnsA[0].proTx, "127.0.0.1:1000", "", mnsA[0].operator_sk) # Now send other two valid proUpServ txes to the mempool, without mining them proupserv2_txid = nodeA.protx_update_service(mnsA[3].proTx, "127.0.0.1:2000", "", mnsA[3].operator_sk) proupserv3_txid = nodeA.protx_update_service(pre_split_mn1.proTx, "127.0.0.1:1001", "", pre_split_mn1.operator_sk) # Send valid proUpReg tx to the mempool operator_to_reuse = nodeA.generateblskeypair()["public"] proupreg1_txid = nodeA.protx_update_registrar(mnsA[4].proTx, operator_to_reuse, "", "") # Try sending another one, reusing the operator key used by another mempool proTx self.log.info( "Testing proUpReg in-mempool duplicate-operator-key rejection...") assert_raises_rpc_error(-26, "protx-dup", nodeA.protx_update_registrar, mnsA[5].proTx, mempool_dmn1.operator_pk, "", "") # Now send other two valid proUpServ txes to the mempool, without mining them new_voting_address = nodeA.getnewaddress() proupreg2_txid = nodeA.protx_update_registrar(mnsA[5].proTx, "", new_voting_address, "") proupreg3_txid = nodeA.protx_update_registrar(pre_split_mn1.proTx, "", new_voting_address, "") # Send two valid proUpRev txes to the mempool, without mining them self.log.info("Revoking two masternodes...") prouprev1_txid = nodeA.protx_revoke(mnsA[6].proTx, mnsA[6].operator_sk) prouprev2_txid = nodeA.protx_revoke(pre_split_mn2.proTx, pre_split_mn2.operator_sk) # Now nodeA has 4 proReg txes in its mempool, 3 proUpServ txes, 3 proUpReg txes, and 2 proUpRev mempoolA = nodeA.getrawmempool() assert mempool_dmn1.proTx in mempoolA assert mempool_dmn2.proTx in mempoolA assert mempool_dmn3.proTx in mempoolA assert mempool_dmn4.proTx in mempoolA assert proupserv1_txid in mempoolA assert proupserv2_txid in mempoolA assert proupserv3_txid in mempoolA assert proupreg1_txid in mempoolA assert proupreg2_txid in mempoolA assert proupreg3_txid in mempoolA assert prouprev1_txid in mempoolA assert prouprev2_txid in mempoolA assert_equal(nodeA.getblockcount(), 208) # # -- CHAIN B -- # collateral_addr = nodeB.getnewaddress() self.log.info("Registering masternodes on chain B...") # Register first the 3 nodes that conflict with the mempool of nodes[0] # mine one block after each registration for dmn in [dmn_A1, dmn_A2, dmn_A3]: self.register_masternode(nodeB, dmn, collateral_addr) mnsB.append(dmn) nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Pick the proReg for the first MN registered on chain A, and replay it on chain B self.log.info("Replaying a masternode on a different chain...") mnsA.remove(pre_split_mn1) mnsA.remove(pre_split_mn2) replay_mn = mnsA.pop(0) mnsB.append(replay_mn) # same proTx hash nodeB.sendrawtransaction( nodeA.getrawtransaction(replay_mn.proTx, False)) nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Now pick a proReg for another MN registered on chain A, and re-register it on chain B self.log.info("Re-registering a masternode on a different chain...") rereg_mn = random.choice(mnsA) mnsA.remove(rereg_mn) self.register_masternode(nodeB, rereg_mn, collateral_addr) mnsB.append(rereg_mn) # changed proTx hash nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Register 5 more masternodes. One per block. for _ in range(5): dmn = create_new_dmn(free_idx, nodeB, collateral_addr, None) free_idx += 1 self.register_masternode(nodeB, dmn, collateral_addr) mnsB.append(dmn) nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Register one masternode reusing the IP of the proUpServ mempool tx on chainA dmn1000 = create_new_dmn(free_idx, nodeB, collateral_addr, None) free_idx += 1 dmn1000.ipport = "127.0.0.1:1000" mnsB.append(dmn1000) self.register_masternode(nodeB, dmn1000, collateral_addr) # Register one masternode reusing the operator-key of the proUpReg mempool tx on chainA dmnop = create_new_dmn(free_idx, nodeB, collateral_addr, None) free_idx += 1 dmnop.operator_pk = operator_to_reuse mnsB.append(dmnop) self.register_masternode(nodeB, dmnop, collateral_addr) # Then mine 10 more blocks on chain B nodeB.generate(10) self.check_mn_list_on_node(1, mnsB) self.log.info("Masternodes registered on chain B.") assert_equal(nodeB.getblockcount(), 222) # # -- RECONNECT -- # # Reconnect and sync (give it some more time) self.log.info("Reconnecting nodes...") self.connect_all() self.sync_blocks(wait=3, timeout=180) # Both nodes have the same list (mnB) self.log.info("Checking masternode list...") self.check_mn_list_on_node(0, mnsB) self.check_mn_list_on_node(1, mnsB) self.log.info("Checking mempool...") mempoolA = nodeA.getrawmempool() # The first mempool proReg tx has been removed from nodeA's mempool due to # conflicts with the masternodes of chain B, now connected. # The fourth mempool proReg tx has been removed because the collateral it # was referencing has been disconnected. assert mempool_dmn1.proTx not in mempoolA assert mempool_dmn2.proTx in mempoolA assert mempool_dmn3.proTx in mempoolA assert mempool_dmn4.proTx not in mempoolA # The first mempool proUpServ tx has been removed as the IP (port=1000) is # now used by a newly connected masternode. # The second mempool proUpServ tx has been removed as it was meant to update # a masternode that is not in the deterministic list anymore. assert proupserv1_txid not in mempoolA assert proupserv2_txid not in mempoolA assert proupserv3_txid in mempoolA # The first mempool proUpReg tx has been removed as the operator key is # now used by a newly connected masternode. # The second mempool proUpReg tx has been removed as it was meant to update # a masternode that is not in the deterministic list anymore. assert proupreg1_txid not in mempoolA assert proupreg2_txid not in mempoolA assert proupreg3_txid in mempoolA # The frist mempool proUpRev tx has been removed as it was meant to revoke # a masternode that is not in the deterministic list anymore. assert prouprev1_txid not in mempoolA assert prouprev2_txid in mempoolA # The mempool contains also all the ProReg from the disconnected blocks, # except the ones re-registered and replayed on chain B. for mn in mnsA: assert mn.proTx in mempoolA assert rereg_mn.proTx not in mempoolA assert replay_mn.proTx not in mempoolA assert pre_split_mn1.proTx not in mempoolA assert pre_split_mn2.proTx not in mempoolA # Mine a block from nodeA so the mempool txes get included self.log.info("Mining mempool txes...") nodeA.generate(1) self.sync_all() # mempool_dmn2 and mempool_dmn3 have been included mnsB.append(mempool_dmn2) mnsB.append(mempool_dmn3) # proupserv3 has changed the IP of the pre_split masternode 1 # and proupreg3 has changed its voting address mnsB.remove(pre_split_mn1) pre_split_mn1.ipport = "127.0.0.1:1001" pre_split_mn1.voting = new_voting_address mnsB.append(pre_split_mn1) # prouprev2 has revoked pre_split masternode 2 mnsB.remove(pre_split_mn2) pre_split_mn2.revoked() mnsB.append(pre_split_mn2) # the ProReg txes, that were added back to the mempool from the # disconnected blocks, have been mined again mns_all = mnsA + mnsB # Check new mn list self.check_mn_list_on_node(0, mns_all) self.check_mn_list_on_node(1, mns_all) self.log.info("Both nodes have %d registered masternodes." % len(mns_all)) self.log.info("All good.")
def run_test(self): self.disable_mocktime() # Additional connections to miner and owner for nodePos in [self.minerPos, self.controllerPos]: self.connect_to_all(nodePos) miner = self.nodes[self.minerPos] controller = self.nodes[self.controllerPos] dummy_add = controller.getnewaddress("dummy") # Enforce mn payments and reject legacy mns at block 131 self.activate_spork(0, "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT") assert_equal( "success", self.set_spork(self.minerPos, "SPORK_21_LEGACY_MNS_MAX_HEIGHT", 130)) time.sleep(1) assert_equal([130] * self.num_nodes, [ self.get_spork(x, "SPORK_21_LEGACY_MNS_MAX_HEIGHT") for x in range(self.num_nodes) ]) mns = [] # Mine 100 blocks self.log.info("Mining...") miner.generate(110) self.sync_blocks() self.assert_equal_for_all(110, "getblockcount") # Test rejection before enforcement self.log.info( "Testing rejection of ProRegTx before DIP3 activation...") assert_raises_rpc_error(-1, "Evo upgrade is not active yet", self.add_new_dmn, mns, "internal") assert_raises_rpc_error(-1, "Evo upgrade is not active yet", self.add_new_dmn, mns, "fund") # Can create the raw proReg dmn = create_new_dmn(2, controller, dummy_add, None) tx, sig = self.protx_register_ext(miner, controller, dmn, None, False) # but cannot send it assert_raises_rpc_error(-1, "Evo upgrade is not active yet", miner.protx_register_submit, tx, sig) self.log.info("Done. Now mine blocks till enforcement...") # Check that no coin has been locked by the controller yet assert_equal(len(controller.listlockunspent()), 0) # DIP3 activates at block 130. miner.generate(130 - miner.getblockcount()) self.sync_blocks() self.assert_equal_for_all(130, "getblockcount") # -- DIP3 enforced and SPORK_21 active here -- self.wait_until_mnsync_completed() # Create 3 DMNs and init the remote nodes self.log.info("Initializing masternodes...") self.add_new_dmn(mns, "internal") self.add_new_dmn(mns, "external") self.add_new_dmn(mns, "fund") for mn in mns: self.nodes[mn.idx].initmasternode(mn.operator_key, "", True) time.sleep(1) miner.generate(1) self.sync_blocks() # Init the other 3 remote nodes before creating the ProReg tx self.log.info("Initializing more masternodes...") op_keys = [] for i in range(3): idx = 2 + len(mns) + i add_and_key = [] add_and_key.append(miner.getnewaddress("oper-%d-key" % idx)) add_and_key.append(miner.dumpprivkey(add_and_key[0])) self.nodes[idx].initmasternode(add_and_key[1], "", True) op_keys.append(add_and_key) time.sleep(1) # Now send the ProReg txes and check list self.add_new_dmn(mns, "internal", op_keys[0]) self.add_new_dmn(mns, "external", op_keys[1]) self.add_new_dmn(mns, "fund", op_keys[2]) miner.generate(2) self.sync_blocks() time.sleep(1) self.log.info("Masternodes started.") self.check_mn_list(mns) # Check status from remote nodes assert_equal([ self.nodes[idx].getmasternodestatus()['status'] for idx in range(2, self.num_nodes) ], ["Ready"] * (self.num_nodes - 2)) self.log.info("All masternodes ready.") # Restart the controller and check that the collaterals are still locked self.log.info("Restarting controller...") self.restart_controller() time.sleep(1) for mn in mns: if not is_coin_locked_by(controller, mn.collateral): raise Exception( "Collateral %s of mn with idx=%d is not locked" % (mn.collateral, mn.idx)) self.log.info("Collaterals still locked.") # Test collateral spending dmn = mns.pop(randrange(len(mns))) # pop one at random self.log.info("Spending collateral of mn with idx=%d..." % dmn.idx) spend_txid = spend_mn_collateral(controller, dmn) self.sync_mempools([miner, controller]) miner.generate(1) self.sync_blocks() assert_greater_than( miner.getrawtransaction(spend_txid, True)["confirmations"], 0) self.check_mn_list(mns) # Register dmn again, with the collateral of dmn2 # dmn must be added again to the list, and dmn2 must be removed dmn2 = mns.pop(randrange(len(mns))) # pop one at random dmn_keys = [dmn.operator, dmn.operator_key] dmn2_keys = [dmn2.operator, dmn2.operator_key] self.log.info( "Reactivating node %d reusing the collateral of node %d..." % (dmn.idx, dmn2.idx)) mns.append( self.register_new_dmn(dmn.idx, self.minerPos, self.controllerPos, "external", outpoint=dmn2.collateral, op_addr_and_key=dmn_keys)) miner.generate(1) self.sync_blocks() self.check_mn_list(mns) # Now try to register dmn2 again with an already-used IP self.log.info("Trying duplicate IP...") rand_idx = mns[randrange(len(mns))].idx assert_raises_rpc_error(-1, "bad-protx-dup-IP-address", self.register_new_dmn, rand_idx, self.minerPos, self.controllerPos, "fund", op_addr_and_key=dmn2_keys) # Now try with duplicate operator key self.log.info("Trying duplicate operator key...") dmn2b = create_new_dmn(dmn2.idx, controller, dummy_add, dmn_keys) assert_raises_rpc_error(-1, "bad-protx-dup-operator-key", self.protx_register_fund, miner, controller, dmn2b, dummy_add) # Now try with duplicate owner key self.log.info("Trying duplicate owner key...") dmn2c = create_new_dmn(dmn2.idx, controller, dummy_add, dmn2_keys) dmn2c.owner = mns[randrange(len(mns))].owner assert_raises_rpc_error(-1, "bad-protx-dup-owner-key", self.protx_register_fund, miner, controller, dmn2c, dummy_add) # Finally, register it properly. This time setting 10% of the reward for the operator op_rew = { "reward": 10.00, "address": self.nodes[dmn2.idx].getnewaddress() } self.log.info( "Reactivating the node with a new registration (with operator reward)..." ) dmn2c = create_new_dmn(dmn2.idx, controller, dummy_add, dmn2_keys) self.protx_register_fund(miner, controller, dmn2c, dummy_add, op_rew) mns.append(dmn2c) time.sleep(1) self.sync_mempools([miner, controller]) miner.generate(6) self.sync_blocks() json_tx = self.nodes[dmn2c.idx].getrawtransaction(dmn2c.proTx, True) assert_greater_than(json_tx['confirmations'], 0) self.check_proreg_payload(dmn2c, json_tx) self.check_mn_list(mns) # 6 masternodes again # Test payments. # Mine 12 blocks and check that each masternode has been paid exactly twice. # Save last paid masternode. Check that it's the last paid also after the 12 blocks. # Note: dmn2 sends (2 * 0.3 OMEGA) to the operator, and (2 * 2.7 OMEGA) to the owner self.log.info("Testing masternode payments...") last_paid_mn = self.get_last_paid_mn() starting_balances = { "operator": self.get_addr_balance(self.nodes[dmn2c.idx], op_rew["address"]) } for mn in mns: starting_balances[mn.payee] = self.get_addr_balance( controller, mn.payee) miner.generate(12) self.sync_blocks() for mn in mns: bal = self.get_addr_balance(controller, mn.payee) expected = starting_balances[mn.payee] + ( Decimal('6.0') if mn.idx != dmn2c.idx else Decimal('5.4')) if bal != expected: raise Exception("Invalid balance (%s != %s) for node %d" % (bal, expected, mn.idx)) self.log.info("All masternodes paid twice.") assert_equal( self.get_addr_balance(self.nodes[dmn2c.idx], op_rew["address"]), starting_balances["operator"] + Decimal('0.6')) self.log.info("Operator paid twice.") assert_equal(last_paid_mn, self.get_last_paid_mn()) self.log.info("Order preserved.") # Test invalid payment self.wait_until_mnsync_completed() # just to be sure self.log.info("Testing invalid masternode payment...") mn_payee_script = miner.validateaddress( miner.getnewaddress())['scriptPubKey'] block = self.create_block( mn_payee_script, miner.getblock(miner.getbestblockhash(), True)) block.solve() assert_equal(miner.submitblock(bytes_to_hex_str(block.serialize())), "bad-cb-payee") self.log.info("All good.")
def run_test(self): self.disable_mocktime() nodeA = self.nodes[0] nodeB = self.nodes[1] free_idx = 1 # unique id for masternodes. first available. # Enforce mn payments and reject legacy mns at block 202 self.activate_spork(0, "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT") assert_equal("success", self.set_spork(0, "SPORK_21_LEGACY_MNS_MAX_HEIGHT", 201)) time.sleep(1) assert_equal([201] * self.num_nodes, [ self.get_spork(x, "SPORK_21_LEGACY_MNS_MAX_HEIGHT") for x in range(self.num_nodes) ]) # Mine 201 blocks self.log.info("Mining...") nodeA.generate(25) self.sync_blocks() nodeB.generate(25) self.sync_blocks() nodeA.generate(50) self.sync_blocks() nodeB.generate(101) self.sync_blocks() self.assert_equal_for_all(201, "getblockcount") # Register one masternode before the split collateral_addr = nodeA.getnewaddress( ) # for both collateral and payouts pre_split_mn = create_new_dmn(100, nodeA, nodeA.getnewaddress(), None) self.register_masternode(nodeA, pre_split_mn, collateral_addr) nodeA.generate(1) self.sync_blocks() mnsA = [pre_split_mn] mnsB = [pre_split_mn] self.check_mn_list_on_node(0, mnsA) self.check_mn_list_on_node(1, mnsB) self.log.info("Pre-split masternode registered.") # Disconnect the nodes self.disconnect_all() # network splits at block 203 # # -- CHAIN A -- # # Register 5 masternodes, then mine 5 blocks self.log.info("Registering masternodes on chain A...") for _ in range(5): dmn = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 self.register_masternode(nodeA, dmn, collateral_addr) mnsA.append(dmn) nodeA.generate(5) self.check_mn_list_on_node(0, mnsA) self.log.info("Masternodes registered on chain A.") # Lock any utxo with less than 101 confs (e.g. change), so we can resurrect everything for x in nodeA.listunspent(0, 101): nodeA.lockunspent(False, [{"txid": x["txid"], "vout": x["vout"]}]) # Now send a valid proReg tx to the mempool, without mining it mempool_dmn1 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 self.register_masternode(nodeA, mempool_dmn1, collateral_addr) assert mempool_dmn1.proTx in nodeA.getrawmempool() # Try sending a proReg tx with same owner self.log.info("Testing in-mempool duplicate-owner rejection...") dmn_A1 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 dmn_A1.owner = mempool_dmn1.owner assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A1, collateral_addr) assert dmn_A1.proTx not in nodeA.getrawmempool() # Try sending a proReg tx with same operator self.log.info("Testing in-mempool duplicate-operator rejection...") dmn_A2 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 dmn_A2.operator = mempool_dmn1.operator assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A2, collateral_addr) assert dmn_A2.proTx not in nodeA.getrawmempool() # Try sending a proReg tx with same IP self.log.info("Testing proReg in-mempool duplicate-IP rejection...") dmn_A3 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 dmn_A3.ipport = mempool_dmn1.ipport assert_raises_rpc_error(-26, "protx-dup", self.register_masternode, nodeA, dmn_A3, collateral_addr) assert dmn_A3.proTx not in nodeA.getrawmempool() # Now send other 2 valid proReg tx to the mempool, without mining it mempool_dmn2 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 mempool_dmn3 = create_new_dmn(free_idx, nodeA, collateral_addr, None) free_idx += 1 self.register_masternode(nodeA, mempool_dmn2, collateral_addr) self.register_masternode(nodeA, mempool_dmn3, collateral_addr) # Now nodeA has 3 proReg txes in its mempool mempoolA = nodeA.getrawmempool() assert mempool_dmn1.proTx in mempoolA assert mempool_dmn2.proTx in mempoolA assert mempool_dmn3.proTx in mempoolA assert_equal(nodeA.getblockcount(), 207) # # -- CHAIN B -- # collateral_addr = nodeB.getnewaddress() self.log.info("Registering masternodes on chain B...") # Register first the 3 nodes that conflict with the mempool of nodes[0] # mine one block after each registration for dmn in [dmn_A1, dmn_A2, dmn_A3]: self.register_masternode(nodeB, dmn, collateral_addr) mnsB.append(dmn) nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Pick the proReg for the first MN registered on chain A, and replay it on chain B self.log.info("Replaying a masternode on a different chain...") mnsA.remove(pre_split_mn) replay_mn = mnsA.pop(0) mnsB.append(replay_mn) # same proTx hash nodeB.sendrawtransaction( nodeA.getrawtransaction(replay_mn.proTx, False)) nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Now pick a proReg for another MN registered on chain A, and re-register it on chain B self.log.info("Re-registering a masternode on a different chain...") rereg_mn = random.choice(mnsA) mnsA.remove(rereg_mn) self.register_masternode(nodeB, rereg_mn, collateral_addr) mnsB.append(rereg_mn) # changed proTx hash nodeB.generate(1) self.check_mn_list_on_node(1, mnsB) # Register 5 more masternodes. One per block. for _ in range(5): dmn = create_new_dmn(free_idx, nodeB, collateral_addr, None) free_idx += 1 self.register_masternode(nodeB, dmn, collateral_addr) mnsB.append(dmn) nodeB.generate(1) # Then mine 10 more blocks on chain B nodeB.generate(10) self.check_mn_list_on_node(1, mnsB) self.log.info("Masternodes registered on chain B.") assert_equal(nodeB.getblockcount(), 222) # # -- RECONNECT -- # # Reconnect and sync (give it some more time) self.log.info("Reconnecting nodes...") self.connect_all() self.sync_blocks(wait=3, timeout=180) # Both nodes have the same list (mnB) self.log.info("Checking masternode list...") self.check_mn_list_on_node(0, mnsB) self.check_mn_list_on_node(1, mnsB) self.log.info("Both nodes have %d registered masternodes." % len(mnsB)) # The first mempool proReg tx has been removed from nodeA's mempool due to # conflicts with the masternodes of chain B, now connected. self.log.info("Checking mempool...") mempoolA = nodeA.getrawmempool() assert mempool_dmn1.proTx not in mempoolA assert mempool_dmn2.proTx in mempoolA assert mempool_dmn3.proTx in mempoolA # The mempool contains also all the ProReg from the disconnected blocks, # except the ones re-registered and replayed on chain B. for mn in mnsA: assert mn.proTx in mempoolA assert rereg_mn.proTx not in mempoolA assert replay_mn.proTx not in mempoolA assert pre_split_mn.proTx not in mempoolA self.log.info("All good.")