def test_no_blockhash(self):
        txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
        blockhash, = self.nodes[2].generate(1)
        self.sync_all()

        txs = self.nodes[0].listtransactions()
        assert_array_result(txs, {"txid": txid}, {
            "category": "receive",
            "amount": 1,
            "blockhash": blockhash,
            "confirmations": 1,
        })
        assert_equal(self.nodes[0].listsinceblock(), {
            "lastblock": blockhash,
            "removed": [],
            "transactions": txs
        })
        assert_equal(self.nodes[0].listsinceblock(""), {
            "lastblock": blockhash,
            "removed": [],
            "transactions": txs
        })

        # contract test
        co = Contract(self.nodes[2])
        co.call_payable()
        txid = co.call_sendCoinTest(self.nodes[0].getnewaddress(), 1)['txid']
        self.sync_all()
        assert txid in self.nodes[0].getrawmempool()
        blockhash, = self.nodes[2].generate(1)
        blockhash, = self.nodes[2].generate(1)
        assert txid not in self.nodes[2].getrawmempool()
        assert txid not in self.nodes[0].getrawmempool()
        self.sync_all()
        txs = self.nodes[0].listtransactions()
Exemplo n.º 2
0
    def run_test(self):
        txouts = gen_return_txouts()
        relayfee = self.nodes[0].getnetworkinfo()['relayfee']

        txids = []
        utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91)

        # create a mempool tx that will be evicted
        us0 = utxos.pop()
        inputs = [{"txid": us0["txid"], "vout": us0["vout"]}]
        outputs = {self.nodes[0].getnewaddress(): 0.1}
        tx = self.nodes[0].createrawtransaction(inputs, outputs)
        self.nodes[0].settxfee(
            relayfee)  # specifically fund this tx with low fee
        txF = self.nodes[0].fundrawtransaction(tx)
        self.nodes[0].settxfee(0)  # return to automatic fee selection
        txFS = self.nodes[0].signrawtransaction(txF['hex'])
        txid = self.nodes[0].sendrawtransaction(txFS['hex'])

        relayfee = self.nodes[0].getnetworkinfo()['relayfee']
        base_fee = relayfee * 100
        for i in range(3):
            txids.append([])
            txids[i] = create_lots_of_big_transactions(
                self.nodes[0], txouts, utxos[30 * i:30 * i + 30], 30,
                (i + 1) * base_fee)

        # by now, the tx should be evicted, check confirmation state
        assert (txid not in self.nodes[0].getrawmempool())
        txdata = self.nodes[0].gettransaction(txid)
        assert (txdata['confirmations'] == 0)  # confirmation should still be 0

        # 存在依赖关系的合约交易,内存池满时的情况
        self.nodes[0].generate(5)
        assert_equal(self.nodes[0].getrawmempool(), [])

        cts = []
        for i in range(1600):
            ct = Contract(self.nodes[0], self.options.tmpdir, debug=False)
            cts.append(ct)
        for ct in cts:
            ct.call_payable(amount=100)

        for ct in cts:
            # print(self.nodes[0].getmempoolinfo())
            reason = ct.call_doubleSpendTest(self.nodes[0].getnewaddress(),
                                             amount=0,
                                             throw_exception=False).reason()
            if reason:
                # 这里表示mempool满了,把payable的交易都移除了,call_doubleSpendTest里边有send调用,所以没钱
                assert_contains(reason, "mempool full")
Exemplo n.º 3
0
 def test_double_spend(self,mineblock = True):
     self.log.info("test double spend")
     node = self.nodes[0]
     ct = Contract(node)
     [ct.call_payable(amount = 100) for i in range(10)]
     node.generate(2)
     self.sync_all()
     addr = self.nodes[1].getnewaddress()
     addr2 = self.nodes[1].getnewaddress()
     contract_balance = ct.get_balance()
     for i in range(10):
         ct.call_reorgTest(addr,addr2,amount = 0,debug = False)
         # print("before:",ct.get_balance(), node.getbalanceof(addr), node.getbalanceof(addr2))
         # self.sync_all()
         if mineblock:
             self.nodes[1].generate(random.randint(1,4))
             self.sync_all()
         # print("after:",ct.get_balance(), node.getbalanceof(addr), node.getbalanceof(addr2))
     self.sync_all()
     node.generate(2)
     self.sync_all()
     print(ct.get_balance(),node.getbalanceof(addr),node.getbalanceof(addr2))
     addr_balance = node.getbalanceof(addr)
     addr2_balance = node.getbalanceof(addr2)
     if not mineblock:
         if addr_balance == 0:
             assert_equal(addr2_balance, 10)
         else:
             assert_equal(addr_balance, 10)
         assert_equal(ct.get_balance(),contract_balance - 10)
     else:
         assert_equal(ct.get_balance(), contract_balance - addr_balance - addr2_balance)
Exemplo n.º 4
0
    def run_test(self):

        #prepare some coins for multiple *rawtransaction commands
        self.nodes[2].generate(10)
        self.sync_all()
        self.nodes[0].generate(10)
        self.sync_all()
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5)
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0)
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),5.0)
        self.sync_all()
        self.nodes[0].generate(5)
        self.sync_all()

        #########################################
        # sendrawtransaction with missing input #
        #########################################
        inputs  = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1}] #won't exists
        outputs = { self.nodes[0].getnewaddress() : 4.998 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        rawtx   = self.nodes[2].signrawtransaction(rawtx)

        # This will raise an exception since there are missing inputs
        assert_raises_rpc_error(-26, "vin-not-found", self.nodes[2].sendrawtransaction, rawtx['hex'])

        #########################
        # RAW TX MULTISIG TESTS #
        #########################
        # 2of2 test
        addr1 = self.nodes[2].getnewaddress()
        addr2 = self.nodes[2].getnewaddress()

        addr1Obj = self.nodes[2].validateaddress(addr1)
        addr2Obj = self.nodes[2].validateaddress(addr2)

        mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])
        mSigObjValid = self.nodes[2].validateaddress(mSigObj)

        #use balance deltas instead of absolute values
        bal = self.nodes[2].getbalance()

        # send 1.2 BTC to msig adr
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(self.nodes[2].getbalance(), bal+Decimal('1.20000000')) #node2 has both keys of the 2of2 ms addr., tx should affect the balance


        # 2of3 test from different nodes
        bal = self.nodes[2].getbalance()
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[2].getnewaddress()
        addr3 = self.nodes[2].getnewaddress()

        addr1Obj = self.nodes[1].validateaddress(addr1)
        addr2Obj = self.nodes[2].validateaddress(addr2)
        addr3Obj = self.nodes[2].validateaddress(addr3)

        mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']])
        mSigObjValid = self.nodes[2].validateaddress(mSigObj)

        txId = self.nodes[0].sendtoaddress(mSigObj, 2.2)
        decTx = self.nodes[0].gettransaction(txId)
        rawTx = self.nodes[0].decoderawtransaction(decTx['hex'])
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        #THIS IS A INCOMPLETE FEATURE
        #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION
        assert_equal(self.nodes[2].getbalance(), bal) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable

        txDetails = self.nodes[0].gettransaction(txId, True)
        rawTx = self.nodes[0].decoderawtransaction(txDetails['hex'])
        vout = False
        for outpoint in rawTx['vout']:
            if outpoint['value'] == Decimal('2.20000000'):
                vout = outpoint
                break

        self.nodes[0].generate(1)
        self.sync_all()
        bal = self.nodes[0].getbalance()
        inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex']}]
        outputs = { self.nodes[0].getnewaddress() : 2.19 }
        rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
        rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs)
        assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx

        rawTxSigned = self.nodes[2].signrawtransaction(rawTx, inputs)
        assert_equal(rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys
        self.nodes[2].sendrawtransaction(rawTxSigned['hex'])
        rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex'])
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(self.nodes[0].getbalance(), bal+Decimal('2600085.00000000')+Decimal('2.19000000')) #block reward + tx

        # 2of2 test for combining transactions
        bal = self.nodes[2].getbalance()
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[2].getnewaddress()

        addr1Obj = self.nodes[1].validateaddress(addr1)
        addr2Obj = self.nodes[2].validateaddress(addr2)

        self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])
        mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])
        mSigObjValid = self.nodes[2].validateaddress(mSigObj)

        txId = self.nodes[0].sendtoaddress(mSigObj, 2.2)
        decTx = self.nodes[0].gettransaction(txId)
        rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex'])
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        assert_equal(self.nodes[2].getbalance(), bal) # the funds of a 2of2 multisig tx should not be marked as spendable

        txDetails = self.nodes[0].gettransaction(txId, True)
        rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex'])
        vout = False
        for outpoint in rawTx2['vout']:
            if outpoint['value'] == Decimal('2.20000000'):
                vout = outpoint
                break

        self.nodes[0].generate(1)
        self.sync_all()
        bal = self.nodes[0].getbalance()
        inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex']}]
        outputs = { self.nodes[0].getnewaddress() : 2.19 }
        rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs)
        rawTxPartialSigned1 = self.nodes[1].signrawtransaction(rawTx2, inputs)
        self.log.info(rawTxPartialSigned1)
        assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx

        rawTxPartialSigned2 = self.nodes[2].signrawtransaction(rawTx2, inputs)
        self.log.info(rawTxPartialSigned2)
        assert_equal(rawTxPartialSigned2['complete'], False) #node2 only has one key, can't comp. sign the tx
        rawTxComb = self.nodes[2].combinerawtransaction([rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']])
        self.log.info(rawTxComb)
        self.nodes[2].sendrawtransaction(rawTxComb)
        rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(self.nodes[0].getbalance(), bal+Decimal('2600085.00000000')+Decimal('2.19000000')) #block reward + tx

        # getrawtransaction tests
        # 1. valid parameters - only supply txid
        txHash = rawTx["hash"]
        assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex'])

        # 2. valid parameters - supply txid and 0 for non-verbose
        assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex'])

        # 3. valid parameters - supply txid and False for non-verbose
        assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex'])

        # 4. valid parameters - supply txid and 1 for verbose.
        # We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
        assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex'])

        # 5. valid parameters - supply txid and True for non-verbose
        assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex'])

        # 6. invalid parameters - supply txid and string "Flase"
        assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase")

        # 7. invalid parameters - supply txid and empty array
        assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, [])

        # 8. invalid parameters - supply txid and empty dict
        assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {})

        inputs  = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}]
        outputs = { self.nodes[0].getnewaddress() : 1 }
        rawtx   = self.nodes[0].createrawtransaction(inputs, outputs)
        decrawtx= self.nodes[0].decoderawtransaction(rawtx)
        assert_equal(decrawtx['vin'][0]['sequence'], 1000)

        # 9. invalid parameters - sequence number out of range
        inputs  = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}]
        outputs = { self.nodes[0].getnewaddress() : 1 }
        assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs)

        # 10. invalid parameters - sequence number out of range
        inputs  = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}]
        outputs = { self.nodes[0].getnewaddress() : 1 }
        assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs)

        inputs  = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}]
        outputs = { self.nodes[0].getnewaddress() : 1 }
        rawtx   = self.nodes[0].createrawtransaction(inputs, outputs)
        decrawtx= self.nodes[0].decoderawtransaction(rawtx)
        assert_equal(decrawtx['vin'][0]['sequence'], 4294967294)

        # 普通交易不能使用合约的UTXO
        node = self.nodes[0]
        ct = Contract(node)
        txid = ct.call_payable(amount = 100).txid
        vout = node.getrawtransaction(txid, 1)["vout"]
        nA = next(i for i, vout in enumerate(node.getrawtransaction(txid, 1)["vout"]) if
                  vout["value"] == Decimal("100"))
        inputs = []
        inputs.append({"txid": txid, "vout": nA})
        outputs = {}
        addr = self.nodes[0].getnewaddress()
        outputs[addr] = Decimal("10")
        signed = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs))
        assert_raises_rpc_error(-26, 'mandatory-script-verify-flag-failed (Opcode missing or not understood)',
                                self.nodes[0].sendrawtransaction, signed["hex"])
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    def test_publish_fork_with_utxo(self, is_contract_output=False):
        '''
             ab0[utxo1]
          /         \
        aa1 [ca1]   bb1 [cb1]
         |           |
        aa2         bb2
         |           |
        aa3         bb3
                     |
                    bb4
                     |
                    ...
        :param contract_output:
        :return:
        '''
        hex_content = get_contract_hex(self.contract_file)
        coster = self.node0.getnewaddress()
        if is_contract_output:
            ct = Contract(self.node0, self.options.tmpdir, debug=False)
            tmp_tx1 = ct.call_payable(amount=2000)['txid']
            tmp_tx2 = ct.call_sendCoinTest(coster, 1000)['txid']
            # print(ct.publish_txid, tmp_tx1, tmp_tx2)
        else:
            self.node0.sendtoaddress(coster, 1000)
        self.node0.generate(2)
        self.sync_all()
        blocks_num = self.node1.getblockcount()
        '''
        分割成2个网络,然后各自用同一个utxo发布合约
        '''
        self.split_network()
        sender_pub = self.node0.validateaddress(coster)['pubkey']
        sender_pri = self.node0.dumpprivkey(coster)
        amount = 0
        changeaddress = self.node0.getnewaddress()
        result = self.publish_contract(self.node0, hex_content, coster,
                                       sender_pub, sender_pri, amount,
                                       changeaddress)
        # print(result)
        txid_a1 = result['txid']
        contract_a1 = result['contractaddress']
        # 第一组节点同步,这是该组链高度应该为12
        block_a1, block_a2 = self.node0.generate(2)
        self.sync_all([self.nodes[:2], self.nodes[2:]])
        assert_equal(self.node1.getblockcount(), blocks_num + 2)
        assert_equal(self.node1.getbestblockhash(), block_a2)
        last_block_hash = block_a2

        # 第二组开始发布合约
        amount = 1
        changeaddress = self.node2.getnewaddress()
        result = self.publish_contract(self.node2, hex_content, coster,
                                       sender_pub, sender_pri, amount,
                                       changeaddress)
        # print(result)
        txid_b1 = result['txid']
        contract_b1 = result['contractaddress']
        # 第二组节点同步,这是该组链高度应该为22
        block_b13 = self.node2.generate(12)[11]
        self.sync_all([self.nodes[:2], self.nodes[2:]])
        assert_equal(self.node2.getblockcount(), blocks_num + 12)
        assert_equal(self.node2.getbestblockhash(), block_b13)

        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))
        self.join_network()
        for i in range(4):
            print(i, self.nodes[i].getblockcount(),
                  int(self.nodes[i].getchaintipwork(), 16))
        # 确认存在分叉存在,并且主链为22个区块的
        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 + 12 + len(blocks))
        assert_equal(self.node2.getbestblockhash(),
                     block_b13 if not blocks else blocks[-1])
        self.node0.gettransaction(txid_a1)
        # 确认分叉上的合约在主链上不能被调用
        assert_raises_rpc_error(
            -1, "CallContractReal => GetContractInfo fail, contractid is " +
            contract_a1, self.node0.callcontract, True, 1, contract_a1,
            self.node0.getnewaddress(), "payable")
        # 主链合约是可以调用的
        self.node0.callcontract(True, 1, contract_b1,
                                self.node0.getnewaddress(), "payable")
        # 未完成的用例,下面的有问题,先屏蔽
        # '''
        # listsinceblock(lastblockhash) should now include txid_a1, as seen from nodes[0]
        # 这里只有node0节点才成功,换成其他节点时失败的,这不应该
        # '''
        lsbres = self.nodes[0].listsinceblock(last_block_hash)
        assert any(
            tx['txid'] == txid_a1
            for tx in lsbres['removed'])  # 这里只有node0节点才成功,换成其他节点时失败的,这不应该

        # but it should not include 'removed' if include_removed=false
        lsbres2 = self.nodes[0].listsinceblock(blockhash=last_block_hash,
                                               include_removed=False)
        assert 'removed' not in lsbres2
Exemplo n.º 7
0
    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 run_test(self):
        self.nodes[0].generate(2)
        # Simple send, 0 to 1:
        txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
        self.sync_all()
        assert_array_result(self.nodes[0].listtransactions(), {"txid": txid}, {
            "category": "send",
            "account": "",
            "amount": Decimal("-10"),
            "confirmations": 0
        })
        assert_array_result(self.nodes[1].listtransactions(), {"txid": txid}, {
            "category": "receive",
            "account": "",
            "amount": Decimal("10"),
            "confirmations": 0
        })
        # mine a block, confirmations should change:
        self.nodes[0].generate(1)
        self.sync_all()
        array0 = [
            o
            for i, o in enumerate(self.nodes[0].listtransactions("*", 1000, 0))
            if o["txid"] == txid
        ]
        array1 = [
            o
            for i, o in enumerate(self.nodes[1].listtransactions("*", 1000, 0))
            if o["txid"] == txid
        ]
        assert_array_result(array0, {"txid": txid}, {
            "category": "send",
            "account": "",
            "amount": Decimal("-10"),
            "confirmations": 1
        })
        assert_array_result(array1, {"txid": txid}, {
            "category": "receive",
            "account": "",
            "amount": Decimal("10"),
            "confirmations": 1
        })

        # send-to-self:
        txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
        assert_array_result(self.nodes[0].listtransactions(), {
            "txid": txid,
            "category": "send"
        }, {"amount": Decimal("-0.2")})
        assert_array_result(self.nodes[0].listtransactions(), {
            "txid": txid,
            "category": "receive"
        }, {"amount": Decimal("0.2")})

        # sendmany from node1: twice to self, twice to node2:
        send_to = {
            self.nodes[0].getnewaddress(): 0.11,
            self.nodes[1].getnewaddress(): 0.22,
            self.nodes[0].getaccountaddress("from1"): 0.33,
            self.nodes[1].getaccountaddress("toself"): 0.44
        }

        sender = self.nodes[1].getnewaddress("1")
        for i in range(4):
            self.nodes[1].sendtoaddress(sender, 0.5)
        self.nodes[1].generate(1)
        txid = self.nodes[1].sendmany("1", send_to)
        self.sync_all()
        assert_array_result(self.nodes[1].listtransactions(), {
            "category": "send",
            "amount": Decimal("-0.11")
        }, {"txid": txid})
        assert_array_result(self.nodes[0].listtransactions(), {
            "category": "receive",
            "amount": Decimal("0.11")
        }, {"txid": txid})
        assert_array_result(self.nodes[1].listtransactions(), {
            "category": "send",
            "amount": Decimal("-0.22")
        }, {"txid": txid})
        assert_array_result(self.nodes[1].listtransactions(), {
            "category": "receive",
            "amount": Decimal("0.22")
        }, {"txid": txid})
        assert_array_result(self.nodes[1].listtransactions(), {
            "category": "send",
            "amount": Decimal("-0.33")
        }, {"txid": txid})
        assert_array_result(self.nodes[0].listtransactions(), {
            "category": "receive",
            "amount": Decimal("0.33")
        }, {
            "txid": txid,
            "account": "from1"
        })
        assert_array_result(self.nodes[1].listtransactions(), {
            "category": "send",
            "amount": Decimal("-0.44")
        }, {
            "txid": txid,
            "account": "1"
        })
        assert_array_result(self.nodes[1].listtransactions(), {
            "category": "receive",
            "amount": Decimal("0.44")
        }, {
            "txid": txid,
            "account": "toself"
        })

        multisig = self.nodes[1].createmultisig(
            1, [self.nodes[1].getnewaddress()])
        self.nodes[0].importaddress(multisig["redeemScript"], "watchonly",
                                    False, True)
        txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1)
        self.nodes[1].generate(1)
        self.sync_all()
        assert (len(self.nodes[0].listtransactions("watchonly", 100, 0,
                                                   False)) == 0)
        assert_array_result(
            self.nodes[0].listtransactions("watchonly", 100, 0, True), {
                "category": "receive",
                "amount": Decimal("0.1")
            }, {
                "txid": txid,
                "account": "watchonly"
            })

        self.run_rbf_opt_in_test()

        # add contract test
        print(self.nodes[0].getbalance())
        co = Contract(self.nodes[0], self.options.tmpdir)
        txid, contract_id = co.publish_txid, co.contract_id
        co.call_payable()
        self.sync_all()
        array0 = [
            o for i, o in enumerate(self.nodes[0].listtransactions())
            if o["txid"] == txid
        ]
        assert array0 != []
        txid = co.call_sendCoinTest(self.nodes[1].getnewaddress(), 1)['txid']
        self.sync_all()
        assert [
            o for i, o in enumerate(self.nodes[1].listtransactions())
            if o["txid"] == txid
        ] != []
Exemplo n.º 9
0
    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)
    def run_test(self):
        self.nodes[1].generate(2)
        sync_blocks(self.nodes)
        balance = self.nodes[0].getbalance()
        contract_obj = Contract(self.nodes[0], self.options.tmpdir)  #构造合约
        txA = contract_obj.publish_txid  #发布合约的交易ID
        txB = contract_obj.call_payable(amount=1)['txid']
        txC = contract_obj.call_payable(amount=1)['txid']
        sync_mempools(self.nodes)
        self.nodes[1].generate(1)

        sync_blocks(self.nodes)
        newbalance = self.nodes[0].getbalance()
        print(balance - newbalance)
        assert (balance - newbalance < Decimal("19.67900001")
                )  # no more than fees lost
        balance = newbalance

        # Disconnect nodes so node0's transactions don't get into node1's mempool
        disconnect_nodes(self.nodes[0], 1)

        # Identify the contract outputs
        # 分别获取发布合约与调用合约的交易输出
        nA, vA = next((i, vout["value"])
                      for i, vout in enumerate(self.nodes[0].getrawtransaction(
                          txA, 1)["vout"]) if vout["value"] != Decimal("1"))
        nB, vB = next((i, vout["value"])
                      for i, vout in enumerate(self.nodes[0].getrawtransaction(
                          txB, 1)["vout"]) if vout["value"] != Decimal("1"))
        nC, vC = next((i, vout["value"])
                      for i, vout in enumerate(self.nodes[0].getrawtransaction(
                          txC, 1)["vout"]) if vout["value"] != Decimal("1"))

        # self.stop_node(0)
        # self.start_node(0, extra_args=["-minrelaytxfee=0.001"])
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        assert_equal(self.nodes[0].getbalance(), balance)
        inputs = []
        # spend 9985.026 + 9997.646 mgc outputs from txD and txE
        # 花费掉vA和vB
        inputs.append({"txid": txA, "vout": nA})
        inputs.append({"txid": txB, "vout": nB})
        outputs = {}
        outputs[self.nodes[0].getnewaddress()] = Decimal(int(vA))
        outputs[self.nodes[1].getnewaddress()] = Decimal(int(vB))
        signed = self.nodes[0].signrawtransaction(
            self.nodes[0].createrawtransaction(inputs, outputs))
        txAB1 = self.nodes[0].sendrawtransaction(signed["hex"])

        # Identify the 9985mgc output
        # 找到va的输出
        nAB, vAB = next((i, vout["value"]) for i, vout in enumerate(
            self.nodes[0].getrawtransaction(txAB1, 1)["vout"])
                        if vout["value"] == Decimal(int(vA)))

        # Create a child tx spending AB1 and C
        inputs = []
        inputs.append({"txid": txAB1, "vout": nAB})
        inputs.append({"txid": txC, "vout": nC})
        outputs = {}
        addrABC2 = self.nodes[0].getnewaddress()
        outputs[addrABC2] = Decimal("19977")
        signed2 = self.nodes[0].signrawtransaction(
            self.nodes[0].createrawtransaction(inputs, outputs))
        txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"])

        # In mempool txs from self should increase balance from change
        newbalance = self.nodes[0].getbalance()
        print(
            newbalance, balance, balance - newbalance, newbalance -
            (balance - Decimal(str(vA + vB + vC)) + Decimal("19977")))
        assert_equal(newbalance,
                     balance - Decimal(str(vA + vB + vC)) + Decimal("19977"))
        balance = newbalance

        # Restart the node with a higher min relay fee so the parent tx is no longer in mempool
        # TODO: redo with eviction
        self.stop_node(0)
        self.start_node(0, extra_args=["-minrelaytxfee=10.00000"])

        # Verify txs no longer in either node's mempool
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        assert_equal(len(self.nodes[1].getrawmempool()), 0)

        # Not in mempool txs from self should only reduce balance
        # inputs are still spent, but change not received
        newbalance = self.nodes[0].getbalance()
        assert_equal(newbalance, balance - Decimal("19977"))
        # Unconfirmed received funds that are not in mempool, also shouldn't show
        # up in unconfirmed balance
        unconfbalance = self.nodes[0].getunconfirmedbalance(
        ) + self.nodes[0].getbalance()
        assert_equal(unconfbalance, newbalance)
        # Also shouldn't show up in listunspent
        x = self.nodes[0].listunspent(1, 9999999, [addrABC2])
        assert (not txABC2 in [utxo["txid"] for utxo in x])
        balance = newbalance

        # Abandon original transaction and verify inputs are available again
        # including that the child tx was also abandoned
        self.nodes[0].abandontransaction(txAB1)
        newbalance = self.nodes[0].getbalance()
        assert_equal(newbalance, balance + Decimal(str(vA + vB + vC)))
        balance = newbalance

        # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
        self.stop_node(0)
        self.start_node(0, extra_args=["-minrelaytxfee=0.00001"])
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        assert_equal(self.nodes[0].getbalance(), balance)

        # But if its received again then it is unabandoned
        # And since now in mempool, the change is available
        # But its child tx remains abandoned
        # There is currently a bug around this and so this test doesn't work.  See Issue #0060 in testin
        self.nodes[0].sendrawtransaction(signed["hex"])
        newbalance = self.nodes[0].getbalance()
        assert_equal(newbalance,
                     balance - Decimal(str(vA + vB)) + Decimal(int(vA)))
        balance = newbalance

        # Send child tx again so its unabandoned
        self.nodes[0].sendrawtransaction(signed2["hex"])
        newbalance = self.nodes[0].getbalance()
        assert_equal(
            newbalance,
            balance - Decimal(int(vAB)) - Decimal(vC) + Decimal("19977"))
        balance = newbalance

        # Remove using high relay fee again
        self.stop_node(0)
        self.start_node(0, extra_args=["-minrelaytxfee=10.00000"])
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        newbalance = self.nodes[0].getbalance()
        assert_equal(newbalance, balance - Decimal("19977"))
        balance = newbalance

        # Create a double spend of AB1 by spending again from only A's 10 output
        # Mine double spend from node 1
        inputs = []
        inputs.append({"txid": txA, "vout": nA})
        inputs.append({"txid": txC, "vout": nC})
        outputs = {}
        outputs[self.nodes[1].getnewaddress()] = Decimal("19950")
        tx = self.nodes[0].createrawtransaction(inputs, outputs)
        signed = self.nodes[0].signrawtransaction(tx)
        self.nodes[1].sendrawtransaction(signed["hex"])
        self.nodes[1].generate(1)

        connect_nodes(self.nodes[0], 1)
        sync_blocks(self.nodes)

        # Verify that B and C's 10 MGC outputs are available for spending again because AB1 is now conflicted
        newbalance = self.nodes[0].getbalance()
        assert_equal(newbalance, balance + Decimal(vC))
        balance = newbalance

        # There is currently a minor bug around this and so this test doesn't work.  See Issue #7315
        # Invalidate the block with the double spend and B's 10 MGC output should no longer be available
        # Don't think C's should either
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        newbalance = self.nodes[0].getbalance()
        # assert_equal(newbalance, balance - Decimal("10"))
        self.log.info(
            "If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer"
        )
        self.log.info(
            "conflicted has not resumed causing its inputs to be seen as spent.  See Issue #7315"
        )
        self.log.info(str(balance) + " -> " + str(newbalance) + " ?")

        self.stop_node(0)
        self.start_node(0, extra_args=["-minrelaytxfee=0.00001"])
        disconnect_nodes(self.nodes[0], 1)
        node = self.nodes[0]
        node.generate(2)
        assert_equal([], node.getrawmempool())  # make sure mempool is empty
        new_address = node.getnewaddress()
        balance = node.getbalance()
        contract_balance = node.getbalanceof(contract_obj.contract_id)
        amount = 1
        txD = contract_obj.call_sendCoinTest(new_address, amount,
                                             amount=0)['txid']
        txE = contract_obj.call_sendCoinTest(new_address, amount,
                                             amount=0)['txid']
        txD_fee = self.get_txfee(txD)
        txE_fee = self.get_txfee(txE)
        newbalance = node.getbalance()
        assert_equal(contract_balance,
                     node.getbalanceof(contract_obj.contract_id))

        # 放弃合约的sendcoin交易
        self.stop_node(0)
        self.start_node(0, extra_args=["-minrelaytxfee=10.0000"])
        assert_equal([], node.getrawmempool())  # make sure mempool is empty
        assert_equal(newbalance, balance + txD_fee + txE_fee)
        node.abandontransaction(txD)
        node.abandontransaction(txE)
        assert_equal(node.getbalance(), balance)
        node.generate(2)
        assert_equal(contract_balance,
                     node.getbalanceof(contract_obj.contract_id))
    def test_double_sendcoins(self):
        '''
        测试同一个合约,分别在主链与分叉链上转走全部金额到不同的地址
        理论短链的交易不应该打回到内存池,因为合约没钱
        ab0[contract_ab]
          /         \
        aa1 [cca1]   bb1 [ccb1]
         |           |
        aa2         bb2
         |           |
        aa3         bb3
                     |
                    bb4
                     |
                    ...
        :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)
        ct.call_payable(amount=100)
        print(ct.publish_txid)
        self.sync_all()
        self.node0.generate(2)
        self.sync_all()
        blocks_num = self.node1.getblockcount()
        contract_balance = 100

        # split mgc network
        self.split_network()
        self.node1.generate(2)  # fork
        self.node3.generate(8)  # fork

        # in group 1
        addr_N1 = self.node1.getnewaddress()
        txid1 = ct.call_sendCoinTest(addr_N1, contract_balance, amount=0).txid
        self.node1.generate(2)
        self.sync_all([self.nodes[:2], self.nodes[2:]])
        assert_equal(self.node1.getbalanceof(addr_N1), 100)
        assert_equal(ct.get_balance(), 0)

        # in group 2
        addr_N3 = self.node3.getnewaddress()
        txid2 = ct.call_sendCoinTest(addr_N3,
                                     contract_balance,
                                     amount=0,
                                     exec_node=self.node3).txid
        self.node3.generate(3)
        self.sync_all([self.nodes[:2], self.nodes[2:]])
        assert_equal(self.node3.getbalanceof(addr_N3), 100)
        assert_equal(ct.get_balance(exec_node=self.node3), 0)

        # join network
        print("join network")
        connect_nodes_bi(self.nodes, 1, 2)
        sync_blocks(self.nodes)

        for i in range(4):
            print("mempool:", self.nodes[i].getrawmempool())
        # 确认存在分叉存在
        tips = self.nodes[1].getchaintips()
        print(tips)
        assert_equal(len(tips), self.tips_num + 1)
        self.tips_num += 1

        # assert
        assert_equal(self.node1.getbalanceof(addr_N1), 0)
        assert_equal(self.node3.getbalanceof(addr_N3), contract_balance)
        assert_equal(ct.get_balance(), 0)
        assert_equal(ct.get_balance(exec_node=self.node3), 0)
        assert txid1 not in self.node1.getrawmempool()