def test_callcontract_fork(self, with_send=False, crash_point=1): ''' 调用同一合约,不含send操作与含send操作 ab0[contract_ab] / \ aa1 [cca1] bb1 [ccb1] | | aa2 bb2 | | aa3 bb3 | bb4 | ... :param with_send: :return: ''' self.sync_all() self.node0.generate(2) assert_equal(self.node0.getrawmempool(), []) # make sure mempool empty assert_equal(self.node1.getrawmempool(), []) # make sure mempool empty ct = Contract(self.node1, self.options.tmpdir, debug=False) ct2 = Contract(self.node1, self.options.tmpdir, debug=False) ct2.call_payable(amount=1000) print(ct.publish_txid) self.sync_all() self.node0.generate(2) self.sync_all() blocks_num = self.node1.getblockcount() # split mgc network self.split_network() self.node1.generate(2) # fork self.node3.generate(8) # fork # in group 1 balance = self.node1.getbalance() # tx_ai,tx_a11,tx_a12这3个交易,在合并网络后,应该都会被重新打回内存池中,毕竟是短链 tx_a1 = ct.call_payable(amount=2000)['txid'] tx_a11 = ct.call_contractDataTest(amount=0)['txid'] tx_a12 = ct.call_contractDataTest(amount=0)['txid'] if with_send: tmp_ct = Contract(self.node1, debug=False) print(tmp_ct.publish_txid) # why after this call ,ct balance at node1 is 1980,it should 1990 tx_a13 = ct.call_callOtherContractTest(ct2.contract_id, 'callOtherContractTest', tmp_ct.contract_id, "contractDataTest", amount=0) print("ct balance:", ct.get_balance()) print(tx_a13.txid) print(tx_a1, tx_a11, tx_a12) self.sync_all([self.nodes[:2], self.nodes[2:]]) last_block_hash = self.node1.generate(2)[-1] assert self.node1.getrawmempool() == [] self.sync_all([self.nodes[:2], self.nodes[2:]]) # in group 2 tx_b1 = ct.call_payable(amount=2000, exec_node=self.node3, sender=self.node3.getnewaddress())['txid'] print(tx_b1) self.sync_all([self.nodes[:2], self.nodes[2:]]) self.node3.generate(2) self.sync_all([self.nodes[:2], self.nodes[2:]]) assert tx_b1 not in self.node3.getrawmempool() tx_b11 = ct.call_contractDataTest(amount=0, exec_node=self.node3)['txid'] print("ct balance:", ct.get_balance(exec_node=self.node3)) if with_send: # 这里有两个crash point,下面代码分别对应不同的CP if crash_point == 1: tx_b12 = ct.call_callOtherContractTest(ct2.contract_id, 'callOtherContractTest', ct.contract_id, "contractDataTest", exec_node=self.node3, amount=0) print("ct balance at node3:", ct.get_balance(exec_node=self.node3)) else: # 这里也在node1中的内存池? tx_b12 = ct.call_callOtherContractTest(ct2.contract_id, 'callOtherContractTest', ct.contract_id, "contractDataTest", amount=0) tx_b13 = ct.call_reentrancyTest(amount=0).txid print("tx_b13:", tx_b13) print("ct balance:", ct.get_balance(exec_node=self.node1)) print("ct balance at node3:", ct.get_balance(exec_node=self.node3)) print("ct balance at node1:", ct.get_balance(exec_node=self.node1)) print("tx_b12:", tx_b12.txid) print(tx_b11) block_b16 = self.node3.generate(6)[-1] assert_equal(self.node3.getrawmempool(), []) if with_send and crash_point == 1: assert_equal(self.node1.getrawmempool(), []) elif with_send and crash_point == 2: assert_equal(sorted(self.node1.getrawmempool()), sorted([tx_b12.txid, tx_b13])) self.sync_all([self.nodes[:2], self.nodes[2:]]) # join network more_work_blocks = self.make_more_work_than(3, 1) for i in range(4): print("before join:", i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) print("mempool:", self.nodes[i].getrawmempool()) print("ct balance at node3:", ct.get_balance(exec_node=self.node3)) print("ct balance at node1:", ct.get_balance(exec_node=self.node1)) print("join network") connect_nodes_bi(self.nodes, 1, 2) sync_blocks(self.nodes) print("ct balance at node1:", ct.get_balance(exec_node=self.node1)) print("ct balance at node3:", ct.get_balance(exec_node=self.node3)) for i in range(4): print("mempool:", self.nodes[i].getrawmempool()) if with_send: print( "assert_equal(len(self.node1.getrawmempool()), 5),should {} == 5" .format(len(self.node1.getrawmempool()))) with_send_crash_point2 = len(self.node1.getrawmempool()) for tx in self.node1.getrawmempool(): # tx_ai,tx_a11,tx_a12 if tx == tx_a1: self.log.info("tx_a1 in mempool") elif tx == tx_a11: self.log.info("tx_a11 in mempool") elif tx == tx_a12: self.log.info("tx_a12 in mempool") elif tx == tmp_ct.publish_txid: self.log.info("node1 tmp_ct in mempool") elif tx == tx_a13.txid: self.log.info("tx_a13 in mempool") if crash_point == 2: if tx == tx_b12.txid: self.log.info("tx_b12 in mempool") elif tx == tx_b13: self.log.info("tx_b13 in mempool") # 短链的块内交易必须是打回内存池的,否则可能有bug了 # 这里不能确定具体数量,不好判断 assert len(self.node1.getrawmempool()) >= 5 and len( self.node1.getrawmempool()) < 8 else: print( " assert_equal(len(self.node1.getrawmempool()), 3),should {} == 3" .format(len(self.node1.getrawmempool()))) for tx in self.node1.getrawmempool(): # tx_ai,tx_a11,tx_a12 if tx == tx_a1: self.log.info("tx_a1 in mempool") elif tx == tx_a11: self.log.info("tx_a11 in mempool") elif tx == tx_a12: self.log.info("tx_a12 in mempool") assert_equal(len(self.node1.getrawmempool()), 3) # 短链的块内交易必须是打回内存池的,否则可能有bug了 assert (balance - MINER_REWARD * 2 - 2000) - self.node1.getbalance() < 100 print("node2 ct get_balance:", ct.get_balance(exec_node=self.node2)) bal = 2000 if with_send and crash_point == 1: bal = 2000 - 10 #这里20是因为send都从第一个合约里边去扣了 assert_equal(self.node1.getbalanceof(ct.contract_id), bal) # 减去合约的send调用 assert_equal(self.node0.getbalanceof(ct.contract_id), bal) # 减去合约的send调用 assert_equal( ct.call_get('counter', broadcasting=False, amount=0)['return'][0], 4) # 因为本节点mempool有合约交易,所以应该为4 assert_equal( ct.call_get('counter', broadcasting=False, exec_node=self.node2, amount=0)['return'][0], 2) # 该节点内存池中没有交易哦,所以应该为2 for i in range(4): print("node{} ct2 get_balance:{}".format( i, ct2.get_balance(exec_node=self.nodes[i]))) if with_send: assert_equal(self.node0.getbalanceof(ct2.contract_id), 1000 - 10 if crash_point == 1 else 1000) # 减去合约的send调用 assert_equal(self.node1.getbalanceof(ct2.contract_id), 1000 - 10 if crash_point == 1 else 1000) # 减去合约的send调用 assert_equal(self.node2.getbalanceof(ct2.contract_id), 1000 - 10 if crash_point == 1 else 1000) # 减去合约的send调用 assert_equal(self.node3.getbalanceof(ct2.contract_id), 1000 - 10 if crash_point == 1 else 1000) # 减去合约的send调用 else: assert_equal(self.node0.getbalanceof(ct2.contract_id), 1000) # 减去合约的send调用 assert_equal(self.node1.getbalanceof(ct2.contract_id), 1000) # 减去合约的send调用 assert_equal(self.node2.getbalanceof(ct2.contract_id), 1000) # 减去合约的send调用 assert_equal(self.node3.getbalanceof(ct2.contract_id), 1000) # 减去合约的send调用 for i in range(4): print(i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) tips = self.nodes[0].getchaintips() print(tips) assert_equal(len(tips), self.tips_num + 1) self.tips_num += 1 assert_equal(self.node2.getblockcount(), blocks_num + 16 + len(more_work_blocks)) assert_equal( self.node2.getbestblockhash(), block_b16 if not more_work_blocks else more_work_blocks[-1]) # print(self.node1.gettransaction(tx_a1)) # print(self.node1.gettransaction(tx_a11)) # clear node0's and node1's mempool and check balance self.node1.generate(4) sync_blocks(self.nodes) assert_equal(self.node0.getrawmempool(), []) assert_equal(self.node1.getrawmempool(), []) if with_send and crash_point == 1: assert_equal(self.node1.getbalanceof(ct.contract_id), 4000 - 20) elif with_send and crash_point == 2: # what the he? assert_equal( self.node1.getbalanceof(ct.contract_id), 4000 - 20 if with_send_crash_point2 == 7 else 4000 - 10) else: assert_equal(self.node1.getbalanceof(ct.contract_id), 4000) bal = 4000 if with_send and crash_point == 1: bal = 4000 - 20 #应该是4000- 20 的,但是现在的send都是从第一个合约里扣 elif with_send and crash_point == 2: bal = 4000 - 10 assert_equal(self.node1.getbalanceof(ct.contract_id), bal) assert (balance - MINER_REWARD * 2 - 2000) - self.node1.getbalance() < 100 # In bestchain,ensure contract data is correct for i in range(4): assert_equal( ct.call_get('counter', exec_node=self.nodes[i], sender=self.nodes[i].getnewaddress(), amount=0)['return'][0], 4) # 未完成的用例,下面的有问题,先屏蔽 # ''' # listsinceblock(lastblockhash) should now include txid_a1, as seen from nodes[0] # 这里只有node1节点才成功,换成其他节点时失败的,这不应该 lsbres = self.nodes[1].listsinceblock(last_block_hash) assert any( tx['txid'] == tx_a1 for tx in lsbres['transactions']) # 这里只有node1节点才成功,换成其他节点时失败的,这不应该 # but it should not include 'removed' if include_removed=false lsbres2 = self.nodes[1].listsinceblock(blockhash=last_block_hash, include_removed=False) assert 'removed' not in lsbres2
def test_mix_contract_transaction_fork(self, gen_blocks=False): ''' 在2条分叉链中,混合执行各种交易,然后: 1.不产生块,合并网络 2.产生块,合并网络 :return: ''' self.sync_all() self.node1.generate(2) assert_equal(self.node1.getrawmempool(), []) # make sure mempool empty assert_equal(self.node0.getrawmempool(), []) # make sure mempool empty ct = Contract(self.node0, self.options.tmpdir, debug=False) ct2 = Contract(self.node0, self.options.tmpdir, debug=False) ct2.call_payable(amount=1000) print(ct.publish_txid) self.sync_all() self.node0.generate(2) self.sync_all() blocks_num = self.node0.getblockcount() # split mgc network self.split_network() self.node0.generate(2) # fork self.node2.generate(8) # fork balances = [n.getbalance() for n in self.nodes] # in group 1 # normal transaction sendtxs_a = [ self.node0.sendtoaddress(self.node3.getnewaddress(), 1000) for i in range(5) ] # publish contract transaction ccontracts_a = [ Contract(self.node0, self.options.tmpdir, debug=False) for i in range(5) ] # call contract transaction call_contract_txs_a = [ ct.call_payable(amount=1000).txid for ct in ccontracts_a ] call_contract_txs_a1 = [ ct.call_callOtherContractTest(ccontracts_a[0].contract_id, 'callOtherContractTest', ccontracts_a[-1].contract_id, "contractDataTest").txid for ct in ccontracts_a ] # long mempool chain transaction for i in range(8): result = ccontracts_a[1].call_reentrancyTest(throw_exception=False) ccontracts_a[2].call_maxContractCallTest(2).txid self.sync_all([self.nodes[:2], self.nodes[2:]]) # in group 2 sendtxs_b = [ self.node2.sendtoaddress(self.node1.getnewaddress(), 1000) for i in range(5) ] # publish contract transaction ccontracts_b = [ Contract(self.node2, self.options.tmpdir, debug=False) for i in range(5) ] # call contract transaction call_contract_txs_b = [ ct.call_payable(amount=1000).txid for ct in ccontracts_b ] call_contract_txs_b1 = [ ct.call_callOtherContractTest(ccontracts_b[0].contract_id, 'callOtherContractTest', ccontracts_b[-1].contract_id, "contractDataTest").txid for ct in ccontracts_b ] # long mempool chain transaction for i in range(8): result = ccontracts_b[1].call_reentrancyTest(throw_exception=False) ccontracts_b[2].call_maxContractCallTest(2).txid self.sync_all([self.nodes[:2], self.nodes[2:]]) # join network if gen_blocks: for i in range(4): print("before make_more_work_than:", i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) print("mempool:", self.nodes[i].getrawmempool()) blocks_a = self.node0.generate(2) blocks_b = self.node2.generate(8) more_work_blocks = self.make_more_work_than(2, 0) for i in range(4): print("before join:", i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) print("mempool:", self.nodes[i].getrawmempool()) print("join network") connect_nodes_bi(self.nodes, 0, 2) try: print("sync_mempools.......") sync_mempools(self.nodes, timeout=30) print("sync_mempools done") except Exception as e: print("sync mempool failed,ignore!") sync_blocks(self.nodes) if gen_blocks: for i in range(4): print("mempool:", self.nodes[i].getrawmempool()) for i in range(4): print(i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) tips = self.nodes[0].getchaintips() print("tips:", tips) assert_equal(len(tips), self.tips_num + 1) self.tips_num += 1 # 合并后,节点再次调用合约,该交易应该回被不同组的节点抛弃,因为合约不存在 self.log.info( "when joined,contractCall will throw EXCEPTION because of the contractPublish transaction be droped by different group" ) tx1, tx2 = None, None # make sure contract publish transaction in mempool for i, c in enumerate(ccontracts_a): # sometimes assert failed here if c.publish_txid not in self.node0.getrawmempool(): print("OOPS!!!!!!!OMG!!!!That's IMPOSSABLE") print( "contractPublish transaction {} not in mempool,index is {}.When call will throw exception" .format(c.publish_txid, i)) result = ccontracts_a[2].call_reentrancyTest() if not result.reason(): tx1 = result.txid result = ccontracts_b[2].call_reentrancyTest() if not result.reason(): tx2 = result.txid try: sync_mempools(self.nodes, timeout=30) except Exception as e: print("sync_mempools(self.nodes,timeout = 30) not done") if tx1 and tx2: wait_until(lambda: tx1 not in self.node2.getrawmempool(), timeout=10) wait_until(lambda: tx1 in self.node1.getrawmempool(), timeout=10) if gen_blocks: # 因为tx2是主链交易,块同步后,可以找到合约的 wait_until(lambda: tx2 in self.node1.getrawmempool(), timeout=10) else: wait_until(lambda: tx2 not in self.node1.getrawmempool(), timeout=10) wait_until(lambda: tx2 in self.node3.getrawmempool(), timeout=10) else: print('tx1 and tx2 is None') for i, n in enumerate(self.nodes): try: n.generate(2) except Exception as e: self.log.info( "Don't know why!!node{} generate failed,reason:{}".format( i, repr(e))) raise print("node{} generate done".format(i)) sync_blocks(self.nodes)
def test_mix_contract_transaction_fork(self, gen_blocks=False): ''' 在2条分叉链中,混合执行各种交易,然后: 1.不产生块,合并网络 2.产生块,合并网络 :return: ''' self.sync_all() self.node1.generate(2) assert_equal(self.node1.getrawmempool(), []) # make sure mempool empty assert_equal(self.node0.getrawmempool(), []) # make sure mempool empty ct = Contract(self.node0, self.options.tmpdir, debug=False) ct2 = Contract(self.node0, self.options.tmpdir, debug=False) ct2.call_payable(amount=1000) print(ct.publish_txid) self.sync_all() self.node0.generate(2) self.sync_all() blocks_num = self.node0.getblockcount() # split mgc network self.split_network() self.node0.generate(2) # fork self.node2.generate(8) # fork balances = [n.getbalance() for n in self.nodes] # in group 1 # normal transaction sendtxs_a = [ self.node0.sendtoaddress(self.node3.getnewaddress(), 1000) for i in range(5) ] # publish contract transaction ccontracts_a = [ Contract(self.node0, self.options.tmpdir, debug=False) for i in range(5) ] # call contract transaction call_contract_txs_a = [ ct.call_payable(amount=1000).txid for ct in ccontracts_a ] call_contract_txs_a1 = [ ct.call_callOtherContractTest(ccontracts_a[0].contract_id, 'callOtherContractTest', ccontracts_a[-1].contract_id, "contractDataTest").txid for ct in ccontracts_a ] # long mempool chain transaction for i in range(8): result = ccontracts_a[1].call_reentrancyTest(throw_exception=False) ccontracts_a[2].call_maxContractCallTest(2).txid self.sync_all([self.nodes[:2], self.nodes[2:]]) # in group 2 sendtxs_b = [ self.node2.sendtoaddress(self.node1.getnewaddress(), 1000) for i in range(5) ] # publish contract transaction ccontracts_b = [ Contract(self.node2, self.options.tmpdir, debug=False) for i in range(5) ] # call contract transaction call_contract_txs_b = [ ct.call_payable(amount=1000).txid for ct in ccontracts_b ] call_contract_txs_b1 = [ ct.call_callOtherContractTest(ccontracts_b[0].contract_id, 'callOtherContractTest', ccontracts_b[-1].contract_id, "contractDataTest").txid for ct in ccontracts_b ] # long mempool chain transaction for i in range(8): result = ccontracts_b[1].call_reentrancyTest(throw_exception=False) ccontracts_b[2].call_maxContractCallTest(2).txid self.sync_all([self.nodes[:2], self.nodes[2:]]) # join network if gen_blocks: blocks_a = self.node0.generate(2) blocks_b = self.node2.generate(6) more_work_blocks = self.make_more_work_than(2, 0) for i in range(4): print("before join:", i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) print("mempool:", self.nodes[i].getrawmempool()) print("join network") connect_nodes_bi(self.nodes, 1, 2) try: print("sync_mempools.......") # sync_mempools(self.nodes, timeout=30) print("sync_mempools done") except Exception as e: print("sync mempool failed,ignore!") sync_blocks(self.nodes) if gen_blocks: for i in range(4): print("mempool:", self.nodes[i].getrawmempool()) for i in range(4): print(i, self.nodes[i].getblockcount(), int(self.nodes[i].getchaintipwork(), 16)) tips = self.nodes[0].getchaintips() print("tips:", tips) assert_equal(len(tips), self.tips_num + 1) self.tips_num += 1 # 合并后,节点再次调用合约,该交易应该回被不同组的节点抛弃,因为合约不存在 result = ccontracts_a[2].call_reentrancyTest() if not result.reason(): tx1 = result.txid result = ccontracts_b[2].call_reentrancyTest() if not result.reason(): tx2 = result.txid # tx1 = ccontracts_a[2].call_reentrancyTest().txid # tx2 = ccontracts_b[2].call_reentrancyTest().txid try: sync_mempools(self.nodes, timeout=30) except Exception as e: print("sync_mempools(self.nodes,timeout = 30) not done") # wait_until(lambda: tx1 not in self.node2.getrawmempool(), timeout=10) # wait_until(lambda: tx1 in self.node1.getrawmempool(), timeout=10) # wait_until(lambda: tx2 not in self.node1.getrawmempool(), timeout=10) # wait_until(lambda: tx2 in self.node3.getrawmempool(), timeout=10) for i, n in enumerate(self.nodes): n.generate(2) print("node{} generate done".format(i)) sync_blocks(self.nodes)