Exemple #1
0
 def test_option_feerate(self):
     self.log.info("test feeRate option")
     # Make sure there is exactly one input so coin selection can't skew the result.
     assert_equal(len(self.nodes[1].listunspent()), 1)
     rawtx = self.nodes[1].createrawtransaction(
         [], {self.nodes[1].getnewaddress(): 0.001})
     result = self.nodes[1].fundrawtransaction(
         rawtx)  # uses self.min_relay_tx_fee (set by settxfee)
     result2 = self.nodes[1].fundrawtransaction(
         rawtx, {"feeRate": 2 * float(self.min_relay_tx_fee)})
     result3 = self.nodes[1].fundrawtransaction(
         rawtx, {"feeRate": 10 * float(self.min_relay_tx_fee)})
     result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
     assert_fee_amount(result2['fee'], count_bytes(result2['hex']),
                       2 * result_fee_rate)
     assert_fee_amount(result3['fee'], count_bytes(result3['hex']),
                       10 * result_fee_rate)
Exemple #2
0
    def test_option_feerate(self):
        #######################
        # Test feeRate option #
        #######################

        # Make sure there is exactly one input so coin selection can't skew the result
        assert_equal(len(self.nodes[3].listunspent(1)), 1)

        inputs = []
        outputs = {self.nodes[3].getnewaddress() : 1}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
        result = self.nodes[3].fundrawtransaction(rawtx)  # uses self.min_relay_tx_fee (set by settxfee)
        result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
        result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
        assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
        result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
        assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
        assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
    def run_test(self):
        min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
        # This test is not meant to test fee estimation and we'd like
        # to be sure all txs are sent at a consistent desired feerate
        for node in self.nodes:
            node.settxfee(min_relay_tx_fee)

        # if the fee's positive delta is higher than this value tests will fail,
        # neg. delta always fail the tests.
        # The size of the signature of every input may be at most 2 bytes larger
        # than a minimum sized signature.

        #            = 2 bytes * minRelayTxFeePerByte
        feeTolerance = 2 * min_relay_tx_fee / 1000

        self.nodes[2].generate(1)
        self.sync_all()
        self.nodes[0].generate(121)
        self.sync_all()

        # ensure that setting changePosition in fundraw with an exact match is handled properly
        rawmatch = self.nodes[2].createrawtransaction(
            [], {self.nodes[2].getnewaddress(): 50})
        rawmatch = self.nodes[2].fundrawtransaction(
            rawmatch, {
                "changePosition": 1,
                "subtractFeeFromOutputs": [0]
            })
        assert_equal(rawmatch["changepos"], -1)

        watchonly_address = self.nodes[0].getnewaddress()
        watchonly_pubkey = self.nodes[0].getaddressinfo(
            watchonly_address)["pubkey"]
        watchonly_amount = Decimal(200)
        self.nodes[3].importpubkey(watchonly_pubkey, "", True)
        watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address,
                                                     watchonly_amount)

        # Lock UTXO so nodes[0] doesn't accidentally spend it
        watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid,
                                               watchonly_address)
        self.nodes[0].lockunspent(False, [{
            "txid": watchonly_txid,
            "vout": watchonly_vout
        }])

        self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(),
                                    watchonly_amount / 10)

        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.nodes[0].generate(1)
        self.sync_all()

        ###############
        # simple test #
        ###############
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert (len(dec_tx['vin']) > 0)  #test that we have enough inputs

        ##############################
        # simple test with two coins #
        ##############################
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 2.2}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert (len(dec_tx['vin']) > 0)  #test if we have enough inputs

        ##############################
        # simple test with two coins #
        ##############################
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 2.6}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert (len(dec_tx['vin']) > 0)
        assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')

        ################################
        # simple test with two outputs #
        ################################
        inputs = []
        outputs = {
            self.nodes[0].getnewaddress(): 2.6,
            self.nodes[1].getnewaddress(): 2.5
        }
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert (len(dec_tx['vin']) > 0)
        assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')

        #########################################################################
        # test a fundrawtransaction with a VIN greater than the required amount #
        #########################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert_equal(fee + totalOut,
                     utx['amount'])  #compare vin total and totalout+fee

        #####################################################################
        # test a fundrawtransaction with which will not get a change output #
        #####################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {
            self.nodes[0].getnewaddress(): Decimal(5.0) - fee - feeTolerance
        }
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert_equal(rawtxfund['changepos'], -1)
        assert_equal(fee + totalOut,
                     utx['amount'])  #compare vin total and totalout+fee

        ####################################################
        # test a fundrawtransaction with an invalid option #
        ####################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        assert_raises_rpc_error(-3, "Unexpected key foo",
                                self.nodes[2].fundrawtransaction, rawtx,
                                {'foo': 'bar'})

        # reserveChangeKey was deprecated and is now removed
        assert_raises_rpc_error(
            -3, "Unexpected key reserveChangeKey",
            lambda: self.nodes[2].fundrawtransaction(
                hexstring=rawtx, options={'reserveChangeKey': True}))

        ############################################################
        # test a fundrawtransaction with an invalid change address #
        ############################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        assert_raises_rpc_error(-5,
                                "changeAddress must be a valid franc address",
                                self.nodes[2].fundrawtransaction, rawtx,
                                {'changeAddress': 'foobar'})

        ############################################################
        # test a fundrawtransaction with a provided change address #
        ############################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        change = self.nodes[2].getnewaddress()
        assert_raises_rpc_error(-8, "changePosition out of bounds",
                                self.nodes[2].fundrawtransaction, rawtx, {
                                    'changeAddress': change,
                                    'changePosition': 2
                                })
        rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {
            'changeAddress': change,
            'changePosition': 0
        })
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        out = dec_tx['vout'][0]
        assert_equal(change, out['scriptPubKey']['addresses'][0])

        #########################################################
        # test a fundrawtransaction with a provided change type #
        #########################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        assert_raises_rpc_error(-1, "JSON value is not a string as expected",
                                self.nodes[2].fundrawtransaction, rawtx,
                                {'change_type': None})
        assert_raises_rpc_error(-5, "Unknown change type ''",
                                self.nodes[2].fundrawtransaction, rawtx,
                                {'change_type': ''})
        rawtx = self.nodes[2].fundrawtransaction(rawtx,
                                                 {'change_type': 'bech32'})
        dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
        assert_equal(
            'witness_v0_keyhash',
            dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])

        #########################################################################
        # test a fundrawtransaction with a VIN smaller than the required amount #
        #########################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)

        # 4-byte version + 1-byte vin count + 36-byte prevout then script_len
        rawtx = rawtx[:82] + "0100" + rawtx[84:]

        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
        assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for i, out in enumerate(dec_tx['vout']):
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts += 1
            else:
                assert_equal(i, rawtxfund['changepos'])

        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
        assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])

        assert_equal(matchingOuts, 1)
        assert_equal(len(dec_tx['vout']), 2)

        ###########################################
        # test a fundrawtransaction with two VINs #
        ###########################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)
        utx2 = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{
            'txid': utx['txid'],
            'vout': utx['vout']
        }, {
            'txid': utx2['txid'],
            'vout': utx2['vout']
        }]
        outputs = {self.nodes[0].getnewaddress(): 6.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for out in dec_tx['vout']:
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts += 1

        assert_equal(matchingOuts, 1)
        assert_equal(len(dec_tx['vout']), 2)

        matchingIns = 0
        for vinOut in dec_tx['vin']:
            for vinIn in inputs:
                if vinIn['txid'] == vinOut['txid']:
                    matchingIns += 1

        assert_equal(
            matchingIns,
            2)  #we now must see two vins identical to vins given as params

        #########################################################
        # test a fundrawtransaction with two VINs and two vOUTs #
        #########################################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)
        utx2 = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{
            'txid': utx['txid'],
            'vout': utx['vout']
        }, {
            'txid': utx2['txid'],
            'vout': utx2['vout']
        }]
        outputs = {
            self.nodes[0].getnewaddress(): 6.0,
            self.nodes[0].getnewaddress(): 1.0
        }
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for out in dec_tx['vout']:
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts += 1

        assert_equal(matchingOuts, 2)
        assert_equal(len(dec_tx['vout']), 3)

        ##############################################
        # test a fundrawtransaction with invalid vin #
        ##############################################
        inputs = [{
            'txid':
            "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1",
            'vout': 0
        }]  #invalid vin!
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        assert_raises_rpc_error(-4, "Insufficient funds",
                                self.nodes[2].fundrawtransaction, rawtx)

        ############################################################
        #compare fee of a standard pubkeyhash transaction
        inputs = []
        outputs = {self.nodes[1].getnewaddress(): 1.1}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a standard pubkeyhash transaction with multiple outputs
        inputs = []
        outputs = {
            self.nodes[1].getnewaddress(): 1.1,
            self.nodes[1].getnewaddress(): 1.2,
            self.nodes[1].getnewaddress(): 0.1,
            self.nodes[1].getnewaddress(): 1.3,
            self.nodes[1].getnewaddress(): 0.2,
            self.nodes[1].getnewaddress(): 0.3
        }
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)
        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendmany("", outputs)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a 2of2 multisig p2sh transaction

        # create 2of2 addr
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[1].getnewaddress()

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

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

        inputs = []
        outputs = {mSigObj: 1.1}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a standard pubkeyhash transaction

        # create 4of5 addr
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[1].getnewaddress()
        addr3 = self.nodes[1].getnewaddress()
        addr4 = self.nodes[1].getnewaddress()
        addr5 = self.nodes[1].getnewaddress()

        addr1Obj = self.nodes[1].getaddressinfo(addr1)
        addr2Obj = self.nodes[1].getaddressinfo(addr2)
        addr3Obj = self.nodes[1].getaddressinfo(addr3)
        addr4Obj = self.nodes[1].getaddressinfo(addr4)
        addr5Obj = self.nodes[1].getaddressinfo(addr5)

        mSigObj = self.nodes[1].addmultisigaddress(4, [
            addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'],
            addr4Obj['pubkey'], addr5Obj['pubkey']
        ])['address']

        inputs = []
        outputs = {mSigObj: 1.1}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        # spend a 2of2 multisig transaction over fundraw

        # create 2of2 addr
        addr1 = self.nodes[2].getnewaddress()
        addr2 = self.nodes[2].getnewaddress()

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

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

        # send 1.2 FRC to msig addr
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        oldBalance = self.nodes[1].getbalance()
        inputs = []
        outputs = {self.nodes[1].getnewaddress(): 1.1}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[2].fundrawtransaction(rawtx)

        signedTx = self.nodes[2].signrawtransactionwithwallet(fundedTx['hex'])
        txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        # make sure funds are received at node1
        assert_equal(oldBalance + Decimal('1.10000000'),
                     self.nodes[1].getbalance())

        ############################################################
        # locked wallet test
        self.stop_node(0)
        self.nodes[1].node_encrypt_wallet("test")
        self.stop_node(2)
        self.stop_node(3)

        self.start_nodes()
        # This test is not meant to test fee estimation and we'd like
        # to be sure all txs are sent at a consistent desired feerate
        for node in self.nodes:
            node.settxfee(min_relay_tx_fee)

        connect_nodes_bi(self.nodes, 0, 1)
        connect_nodes_bi(self.nodes, 1, 2)
        connect_nodes_bi(self.nodes, 0, 2)
        connect_nodes_bi(self.nodes, 0, 3)
        # Again lock the watchonly UTXO or nodes[0] may spend it, because
        # lockunspent is memory-only and thus lost on restart
        self.nodes[0].lockunspent(False, [{
            "txid": watchonly_txid,
            "vout": watchonly_vout
        }])
        self.sync_all()

        # drain the keypool
        self.nodes[1].getnewaddress()
        self.nodes[1].getrawchangeaddress()
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 1.1}
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        # fund a transaction that requires a new key for the change output
        # creating the key must be impossible because the wallet is locked
        assert_raises_rpc_error(
            -4, "Keypool ran out, please call keypoolrefill first",
            self.nodes[1].fundrawtransaction, rawtx)

        #refill the keypool
        self.nodes[1].walletpassphrase("test", 100)
        self.nodes[1].keypoolrefill(
            8)  #need to refill the keypool to get an internal change address
        self.nodes[1].walletlock()

        assert_raises_rpc_error(-13, "walletpassphrase",
                                self.nodes[1].sendtoaddress,
                                self.nodes[0].getnewaddress(), 1.2)

        oldBalance = self.nodes[0].getbalance()

        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 1.1}
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawtx)

        #now we need to unlock
        self.nodes[1].walletpassphrase("test", 600)
        signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
        txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
        self.nodes[1].generate(1)
        self.sync_all()

        # make sure funds are received at node1
        assert_equal(oldBalance + Decimal('51.10000000'),
                     self.nodes[0].getbalance())

        ###############################################
        # multiple (~19) inputs tx test | Compare fee #
        ###############################################

        #empty node1, send some small coins from node0 to node1
        self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(),
                                    self.nodes[1].getbalance(), "", "", True)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        for i in range(0, 20):
            self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
        self.nodes[0].generate(1)
        self.sync_all()

        #fund a tx with ~20 small inputs
        inputs = []
        outputs = {
            self.nodes[0].getnewaddress(): 0.15,
            self.nodes[0].getnewaddress(): 0.04
        }
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[1].sendmany("", outputs)
        signedFee = self.nodes[1].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance * 19)  #~19 inputs

        #############################################
        # multiple (~19) inputs tx test | sign/send #
        #############################################

        #again, empty node1, send some small coins from node0 to node1
        self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(),
                                    self.nodes[1].getbalance(), "", "", True)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        for i in range(0, 20):
            self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
        self.nodes[0].generate(1)
        self.sync_all()

        #fund a tx with ~20 small inputs
        oldBalance = self.nodes[0].getbalance()

        inputs = []
        outputs = {
            self.nodes[0].getnewaddress(): 0.15,
            self.nodes[0].getnewaddress(): 0.04
        }
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawtx)
        fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(
            fundedTx['hex'])
        txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(oldBalance + Decimal('50.19000000'),
                     self.nodes[0].getbalance())  #0.19+block reward

        #####################################################
        # test fundrawtransaction with OP_RETURN and no vin #
        #####################################################

        rawtx = "0100000000010000000000000000066a047465737400000000"
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        assert_equal(len(dec_tx['vin']), 0)
        assert_equal(len(dec_tx['vout']), 1)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])

        assert_greater_than(len(dec_tx['vin']), 0)  # at least one vin
        assert_equal(len(dec_tx['vout']), 2)  # one change output added

        ##################################################
        # test a fundrawtransaction using only watchonly #
        ##################################################

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): watchonly_amount / 2}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = self.nodes[3].fundrawtransaction(rawtx,
                                                  {'includeWatching': True})
        res_dec = self.nodes[0].decoderawtransaction(result["hex"])
        assert_equal(len(res_dec["vin"]), 1)
        assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)

        assert ("fee" in result.keys())
        assert_greater_than(result["changepos"], -1)

        ###############################################################
        # test fundrawtransaction using the entirety of watched funds #
        ###############################################################

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): watchonly_amount}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        # Backward compatibility test (2nd param is includeWatching)
        result = self.nodes[3].fundrawtransaction(rawtx, True)
        res_dec = self.nodes[0].decoderawtransaction(result["hex"])
        assert_equal(len(res_dec["vin"]), 2)
        assert (res_dec["vin"][0]["txid"] == watchonly_txid
                or res_dec["vin"][1]["txid"] == watchonly_txid)

        assert_greater_than(result["fee"], 0)
        assert_greater_than(result["changepos"], -1)
        assert_equal(
            result["fee"] + res_dec["vout"][result["changepos"]]["value"],
            watchonly_amount / 10)

        signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"])
        assert (not signedtx["complete"])
        signedtx = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"])
        assert (signedtx["complete"])
        self.nodes[0].sendrawtransaction(signedtx["hex"])
        self.nodes[0].generate(1)
        self.sync_all()

        #######################
        # Test feeRate option #
        #######################

        # Make sure there is exactly one input so coin selection can't skew the result
        assert_equal(len(self.nodes[3].listunspent(1)), 1)

        inputs = []
        outputs = {self.nodes[3].getnewaddress(): 1}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
        result = self.nodes[3].fundrawtransaction(
            rawtx)  # uses min_relay_tx_fee (set by settxfee)
        result2 = self.nodes[3].fundrawtransaction(
            rawtx, {"feeRate": 2 * min_relay_tx_fee})
        result3 = self.nodes[3].fundrawtransaction(
            rawtx, {"feeRate": 10 * min_relay_tx_fee})
        result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
        assert_fee_amount(result2['fee'], count_bytes(result2['hex']),
                          2 * result_fee_rate)
        assert_fee_amount(result3['fee'], count_bytes(result3['hex']),
                          10 * result_fee_rate)

        ################################
        # Test no address reuse occurs #
        ################################

        result3 = self.nodes[3].fundrawtransaction(rawtx)
        res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
        changeaddress = ""
        for out in res_dec['vout']:
            if out['value'] > 1.0:
                changeaddress += out['scriptPubKey']['addresses'][0]
        assert (changeaddress != "")
        nextaddr = self.nodes[3].getnewaddress()
        # Now the change address key should be removed from the keypool
        assert (changeaddress != nextaddr)

        ######################################
        # Test subtractFeeFromOutputs option #
        ######################################

        # Make sure there is exactly one input so coin selection can't skew the result
        assert_equal(len(self.nodes[3].listunspent(1)), 1)

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): 1}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = [
            self.nodes[3].fundrawtransaction(
                rawtx),  # uses min_relay_tx_fee (set by settxfee)
            self.nodes[3].fundrawtransaction(
                rawtx,
                {"subtractFeeFromOutputs": []}),  # empty subtraction list
            self.nodes[3].fundrawtransaction(
                rawtx, {"subtractFeeFromOutputs": [0]
                        }),  # uses min_relay_tx_fee (set by settxfee)
            self.nodes[3].fundrawtransaction(
                rawtx, {"feeRate": 2 * min_relay_tx_fee}),
            self.nodes[3].fundrawtransaction(rawtx, {
                "feeRate": 2 * min_relay_tx_fee,
                "subtractFeeFromOutputs": [0]
            })
        ]

        dec_tx = [
            self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result
        ]
        output = [
            d['vout'][1 - r['changepos']]['value']
            for d, r in zip(dec_tx, result)
        ]
        change = [
            d['vout'][r['changepos']]['value'] for d, r in zip(dec_tx, result)
        ]

        assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee'])
        assert_equal(result[3]['fee'], result[4]['fee'])
        assert_equal(change[0], change[1])
        assert_equal(output[0], output[1])
        assert_equal(output[0], output[2] + result[2]['fee'])
        assert_equal(change[0] + result[0]['fee'], change[2])
        assert_equal(output[3], output[4] + result[4]['fee'])
        assert_equal(change[3] + result[3]['fee'], change[4])

        inputs = []
        outputs = {
            self.nodes[2].getnewaddress(): value
            for value in (1.0, 1.1, 1.2, 1.3)
        }
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = [
            self.nodes[3].fundrawtransaction(rawtx),
            # split the fee between outputs 0, 2, and 3, but not output 1
            self.nodes[3].fundrawtransaction(
                rawtx, {"subtractFeeFromOutputs": [0, 2, 3]})
        ]

        dec_tx = [
            self.nodes[3].decoderawtransaction(result[0]['hex']),
            self.nodes[3].decoderawtransaction(result[1]['hex'])
        ]

        # Nested list of non-change output amounts for each transaction
        output = [[
            out['value'] for i, out in enumerate(d['vout'])
            if i != r['changepos']
        ] for d, r in zip(dec_tx, result)]

        # List of differences in output amounts between normal and subtractFee transactions
        share = [o0 - o1 for o0, o1 in zip(output[0], output[1])]

        # output 1 is the same in both transactions
        assert_equal(share[1], 0)

        # the other 3 outputs are smaller as a result of subtractFeeFromOutputs
        assert_greater_than(share[0], 0)
        assert_greater_than(share[2], 0)
        assert_greater_than(share[3], 0)

        # outputs 2 and 3 take the same share of the fee
        assert_equal(share[2], share[3])

        # output 0 takes at least as much share of the fee, and no more than 2 satoshis more, than outputs 2 and 3
        assert_greater_than_or_equal(share[0], share[2])
        assert_greater_than_or_equal(share[2] + Decimal(2e-8), share[0])

        # the fee is the same in both transactions
        assert_equal(result[0]['fee'], result[1]['fee'])

        # the total subtracted from the outputs is equal to the fee
        assert_equal(share[0] + share[2] + share[3], result[0]['fee'])
Exemple #4
0
    def test_option_feerate(self):
        self.log.info(
            "Test fundrawtxn with explicit fee rates (fee_rate sat/vB and feeRate BTC/kvB)"
        )
        node = self.nodes[3]
        # Make sure there is exactly one input so coin selection can't skew the result.
        assert_equal(len(self.nodes[3].listunspent(1)), 1)
        inputs = []
        outputs = {node.getnewaddress(): 1}
        rawtx = node.createrawtransaction(inputs, outputs)

        result = node.fundrawtransaction(
            rawtx)  # uses self.min_relay_tx_fee (set by settxfee)
        btc_kvb_to_sat_vb = 100000  # (1e5)
        result1 = node.fundrawtransaction(
            rawtx, {"fee_rate": 2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
        result2 = node.fundrawtransaction(
            rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
        result3 = node.fundrawtransaction(
            rawtx,
            {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
        result4 = node.fundrawtransaction(
            rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
        result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
        assert_fee_amount(result1['fee'], count_bytes(result2['hex']),
                          2 * result_fee_rate)
        assert_fee_amount(result2['fee'], count_bytes(result2['hex']),
                          2 * result_fee_rate)
        assert_fee_amount(result3['fee'], count_bytes(result3['hex']),
                          10 * result_fee_rate)
        assert_fee_amount(result4['fee'], count_bytes(result3['hex']),
                          10 * result_fee_rate)

        # With no arguments passed, expect fee of 141 satoshis.
        assert_approx(node.fundrawtransaction(rawtx)["fee"],
                      vexp=0.00000141,
                      vspan=0.00000001)
        # Expect fee to be 10,000x higher when an explicit fee rate 10,000x greater is specified.
        result = node.fundrawtransaction(rawtx, {"fee_rate": 10000})
        assert_approx(result["fee"], vexp=0.0141, vspan=0.0001)

        self.log.info("Test fundrawtxn with invalid estimate_mode settings")
        for k, v in {"number": 42, "object": {"foo": "bar"}}.items():
            assert_raises_rpc_error(
                -3, "Expected type string for estimate_mode, got {}".format(k),
                node.fundrawtransaction, rawtx, {
                    "estimate_mode": v,
                    "conf_target": 0.1,
                    "add_inputs": True
                })
        for mode in ["", "foo", Decimal("3.141592")]:
            assert_raises_rpc_error(
                -8,
                'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"',
                node.fundrawtransaction, rawtx, {
                    "estimate_mode": mode,
                    "conf_target": 0.1,
                    "add_inputs": True
                })

        self.log.info("Test fundrawtxn with invalid conf_target settings")
        for mode in ["unset", "economical", "conservative"]:
            self.log.debug("{}".format(mode))
            for k, v in {"string": "", "object": {"foo": "bar"}}.items():
                assert_raises_rpc_error(
                    -3,
                    "Expected type number for conf_target, got {}".format(k),
                    node.fundrawtransaction, rawtx, {
                        "estimate_mode": mode,
                        "conf_target": v,
                        "add_inputs": True
                    })
            for n in [-1, 0, 1009]:
                assert_raises_rpc_error(
                    -8,
                    "Invalid conf_target, must be between 1 and 1008",  # max value of 1008 per src/policy/fees.h
                    node.fundrawtransaction,
                    rawtx,
                    {
                        "estimate_mode": mode,
                        "conf_target": n,
                        "add_inputs": True
                    })

        self.log.info("Test invalid fee rate settings")
        assert_raises_rpc_error(
            -8, "Invalid fee_rate 0.000 sat/vB (must be greater than 0)",
            node.fundrawtransaction, rawtx, {
                "fee_rate": 0,
                "add_inputs": True
            })
        assert_raises_rpc_error(
            -8,
            "Invalid feeRate 0.00000000 SUGAR/kvB (must be greater than 0)",
            node.fundrawtransaction, rawtx, {
                "feeRate": 0,
                "add_inputs": True
            })
        for param, value in {("fee_rate", 100000), ("feeRate", 1.000)}:
            assert_raises_rpc_error(
                -4,
                "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
                node.fundrawtransaction, rawtx, {
                    param: value,
                    "add_inputs": True
                })
            assert_raises_rpc_error(-3, "Amount out of range",
                                    node.fundrawtransaction, rawtx, {
                                        "fee_rate": -1,
                                        "add_inputs": True
                                    })
            assert_raises_rpc_error(-3, "Amount is not a number or string",
                                    node.fundrawtransaction, rawtx, {
                                        "fee_rate": {
                                            "foo": "bar"
                                        },
                                        "add_inputs": True
                                    })
            assert_raises_rpc_error(-3, "Invalid amount",
                                    node.fundrawtransaction, rawtx, {
                                        "fee_rate": "",
                                        "add_inputs": True
                                    })

        self.log.info(
            "Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed"
        )
        node.fundrawtransaction(rawtx, {
            "fee_rate": 0.99999999,
            "add_inputs": True
        })
        node.fundrawtransaction(rawtx, {
            "feeRate": 0.00000999,
            "add_inputs": True
        })

        self.log.info(
            "- raises RPC error if both feeRate and fee_rate are passed")
        assert_raises_rpc_error(
            -8,
            "Cannot specify both fee_rate (sat/vB) and feeRate (SUGAR/kvB)",
            node.fundrawtransaction, rawtx, {
                "fee_rate": 0.1,
                "feeRate": 0.1,
                "add_inputs": True
            })

        self.log.info(
            "- raises RPC error if both feeRate and estimate_mode passed")
        assert_raises_rpc_error(
            -8, "Cannot specify both estimate_mode and feeRate",
            node.fundrawtransaction, rawtx, {
                "estimate_mode": "economical",
                "feeRate": 0.1,
                "add_inputs": True
            })

        for param in ["feeRate", "fee_rate"]:
            self.log.info(
                "- raises RPC error if both {} and conf_target are passed".
                format(param))
            assert_raises_rpc_error(
                -8,
                "Cannot specify both conf_target and {}. Please provide either a confirmation "
                "target in blocks for automatic fee estimation, or an explicit fee rate."
                .format(param), node.fundrawtransaction, rawtx, {
                    param: 1,
                    "conf_target": 1,
                    "add_inputs": True
                })

        self.log.info(
            "- raises RPC error if both fee_rate and estimate_mode are passed")
        assert_raises_rpc_error(
            -8, "Cannot specify both estimate_mode and fee_rate",
            node.fundrawtransaction, rawtx, {
                "fee_rate": 1,
                "estimate_mode": "economical",
                "add_inputs": True
            })
    def run_test(self):
        # Check that there's no UTXO on none of the nodes
        assert_equal(len(self.nodes[0].listunspent()), 0)
        assert_equal(len(self.nodes[1].listunspent()), 0)
        assert_equal(len(self.nodes[2].listunspent()), 0)

        self.log.info("Mining blocks...")

        self.nodes[0].generate(1)

        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], SUBSIDY)
        assert_equal(walletinfo['balance'], 0)

        self.sync_all(self.nodes[0:3])
        self.nodes[1].generate(101)
        self.sync_all(self.nodes[0:3])

        assert_equal(self.nodes[0].getbalance(), SUBSIDY)
        assert_equal(self.nodes[1].getbalance(), SUBSIDY)
        assert_equal(self.nodes[2].getbalance(), 0)

        # Check that only first and second nodes have UTXOs
        utxos = self.nodes[0].listunspent()
        assert_equal(len(utxos), 1)
        assert_equal(len(self.nodes[1].listunspent()), 1)
        assert_equal(len(self.nodes[2].listunspent()), 0)

        self.log.info("test gettxout")
        confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"]
        # First, outputs that are unspent both in the chain and in the
        # mempool should appear with or without include_mempool
        txout = self.nodes[0].gettxout(
            txid=confirmed_txid, n=confirmed_index, include_mempool=False)
        assert_equal(txout['value'], SUBSIDY)
        txout = self.nodes[0].gettxout(
            txid=confirmed_txid, n=confirmed_index, include_mempool=True)
        assert_equal(txout['value'], SUBSIDY)

        # Send 110 Lotus from 0 to 2 using sendtoaddress call.
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal('110'))
        mempool_txid = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), Decimal('100'))

        self.log.info("test gettxout (second part)")
        # utxo spent in mempool should be visible if you exclude mempool
        # but invisible if you include mempool
        txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False)
        assert_equal(txout['value'], SUBSIDY)
        txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True)
        assert txout is None
        # new utxo from mempool should be invisible if you exclude mempool
        # but visible if you include mempool
        txout = self.nodes[0].gettxout(mempool_txid, 0, False)
        assert txout is None
        txout1 = self.nodes[0].gettxout(mempool_txid, 0, True)
        txout2 = self.nodes[0].gettxout(mempool_txid, 1, True)
        # note the mempool tx will have randomly assigned indices
        # but 100 Lotus will go to node2 and the rest will go to node0
        balance = self.nodes[0].getbalance()
        assert_equal(set([txout1['value'], txout2['value']]),
                     set([Decimal('100'), balance]))
        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], 0)

        # Have node0 mine a block, thus it will collect its own fee.
        self.nodes[0].generate(1)
        self.sync_all(self.nodes[0:3])

        # Exercise locking of unspent outputs
        unspent_0 = self.nodes[2].listunspent()[0]
        unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
        assert_raises_rpc_error(-8, "Invalid parameter, expected locked output",
                                self.nodes[2].lockunspent, True, [unspent_0])
        self.nodes[2].lockunspent(False, [unspent_0])
        assert_raises_rpc_error(-8, "Invalid parameter, output already locked",
                                self.nodes[2].lockunspent, False, [unspent_0])
        assert_raises_rpc_error(-4, "Insufficient funds",
                                self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), Decimal('200'))
        assert_equal([unspent_0], self.nodes[2].listlockunspent())
        self.nodes[2].lockunspent(True, [unspent_0])
        assert_equal(len(self.nodes[2].listlockunspent()), 0)
        assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')",
                                self.nodes[2].lockunspent, False,
                                [{"txid": "0000000000000000000000000000000000", "vout": 0}])
        assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')",
                                self.nodes[2].lockunspent, False,
                                [{"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", "vout": 0}])
        assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
                                self.nodes[2].lockunspent, False,
                                [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0}])
        assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds",
                                self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}])

        # An output should be unlocked when spent
        unspent_0 = self.nodes[1].listunspent()[0]
        self.nodes[1].lockunspent(False, [unspent_0])
        tx = self.nodes[1].createrawtransaction(
            [unspent_0], {self.nodes[1].getnewaddress(): Decimal('100')})
        tx = self.nodes[1].fundrawtransaction(tx)['hex']
        tx = self.nodes[1].signrawtransactionwithwallet(tx)["hex"]
        self.nodes[1].sendrawtransaction(tx)
        assert_equal(len(self.nodes[1].listlockunspent()), 0)

        # Have node1 generate 100 blocks (so node0 can recover the fee)
        self.nodes[1].generate(100)
        self.sync_all(self.nodes[0:3])

        # node0 should end up with 520 Lotus in block rewards plus fees, but
        # minus the 210 plus fees sent to node2 and the burnt fees.
        burned_fees = Decimal('0.000225')
        assert_equal(self.nodes[0].getbalance(), 2 * SUBSIDY - Decimal('210') - burned_fees)
        assert_equal(self.nodes[2].getbalance(), Decimal('210'))

        # Node0 should have two unspent outputs.
        # Create a couple of transactions to send them to node2, submit them through
        # node1, and make sure both node0 and node2 pick them up properly:
        node0utxos = self.nodes[0].listunspent(1)
        assert_equal(len(node0utxos), 2)

        # create both transactions
        txns_to_send = []
        for utxo in node0utxos:
            inputs = []
            outputs = {}
            inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]})
            outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - Decimal('10')
            raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
            txns_to_send.append(
                self.nodes[0].signrawtransactionwithwallet(raw_tx))

        # Have node 1 (miner) send the transactions
        self.nodes[1].sendrawtransaction(
            hexstring=txns_to_send[0]["hex"], maxfeerate=0)
        self.nodes[1].sendrawtransaction(
            hexstring=txns_to_send[1]["hex"], maxfeerate=0)

        # Have node1 mine a block to confirm transactions:
        self.nodes[1].generate(1)
        self.sync_all(self.nodes[0:3])

        assert_equal(self.nodes[0].getbalance(), 0)
        burned_fees = Decimal('0.000225')
        assert_equal(self.nodes[2].getbalance(), 2 * SUBSIDY - Decimal('20') - burned_fees)

        # Verify that a spent output cannot be locked anymore
        spent_0 = {"txid": node0utxos[0]["txid"],
                   "vout": node0utxos[0]["vout"]}
        assert_raises_rpc_error(-8, "Invalid parameter, expected unspent output",
                                self.nodes[0].lockunspent, False, [spent_0])

        # Send 100 Lotus normal
        old_balance = self.nodes[2].getbalance()
        address = self.nodes[0].getnewaddress("test")
        fee_per_byte = Decimal('0.1') / 1000
        self.nodes[2].settxfee(fee_per_byte * 1000)
        txid = self.nodes[2].sendtoaddress(address, Decimal('100'), "", "", False)
        self.nodes[2].generate(1)
        self.sync_all(self.nodes[0:3])
        ctx = FromHex(CTransaction(),
                      self.nodes[2].gettransaction(txid)['hex'])

        node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), old_balance - Decimal('100'),
                                           fee_per_byte, ctx.billable_size())
        assert_equal(self.nodes[0].getbalance(), Decimal('100'))

        # Send 100 Lotus with subtract fee from amount
        txid = self.nodes[2].sendtoaddress(address, Decimal('100'), "", "", True)
        self.nodes[2].generate(1)
        self.sync_all(self.nodes[0:3])
        node_2_bal -= Decimal('100')
        assert_equal(self.nodes[2].getbalance(), node_2_bal)
        node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('200'),
            fee_per_byte, count_bytes(self.nodes[2].gettransaction(txid)['hex']))

        # Sendmany 100 Lotus
        txid = self.nodes[2].sendmany('', {address: Decimal('100')}, 0, "", [])
        self.nodes[2].generate(1)
        self.sync_all(self.nodes[0:3])
        node_0_bal += Decimal('100')
        ctx = FromHex(CTransaction(),
                      self.nodes[2].gettransaction(txid)['hex'])
        node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(
        ), node_2_bal - Decimal('100'), fee_per_byte, ctx.billable_size())
        assert_equal(self.nodes[0].getbalance(), node_0_bal)

        # Sendmany 100 Lotus with subtract fee from amount
        txid = self.nodes[2].sendmany('', {address: Decimal('100')}, 0, "", [address])
        self.nodes[2].generate(1)
        self.sync_all(self.nodes[0:3])
        node_2_bal -= Decimal('100')
        assert_equal(self.nodes[2].getbalance(), node_2_bal)
        ctx = FromHex(CTransaction(),
                      self.nodes[2].gettransaction(txid)['hex'])
        node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(
        ), node_0_bal + Decimal('100'), fee_per_byte, ctx.billable_size())

        self.start_node(3, self.extra_args[3])
        connect_nodes(self.nodes[0], self.nodes[3])
        self.sync_all()

        # check if we can list zero value tx as available coins
        # 1. create raw_tx
        # 2. hex-changed one output to 0.0
        # 3. sign and send
        # 4. check if recipient (node0) can list the zero value tx
        usp = self.nodes[1].listunspent(
            query_options={'minimumAmount': str(SUBSIDY - Decimal('0.2'))})[0]
        inputs = [{"txid": usp['txid'], "vout": usp['vout']}]
        outputs = {self.nodes[1].getnewaddress(): SUBSIDY - Decimal('0.2'),
                   self.nodes[0].getnewaddress(): Decimal('1111')}

        rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace(
            "c0833842", "00000000")  # replace 1111 with 0.0 (int32)
        signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(rawTx)
        decoded_raw_tx = self.nodes[1].decoderawtransaction(
            signed_raw_tx['hex'])
        zero_value_txid = decoded_raw_tx['txid']
        self.nodes[1].sendrawtransaction(signed_raw_tx['hex'])

        self.sync_all()
        self.nodes[1].generate(1)  # mine a block
        self.sync_all()

        # zero value tx must be in listunspents output
        unspent_txs = self.nodes[0].listunspent()
        found = False
        for uTx in unspent_txs:
            if uTx['txid'] == zero_value_txid:
                found = True
                assert_equal(uTx['amount'], Decimal('0'))
        assert found

        # do some -walletbroadcast tests
        self.stop_nodes()
        self.start_node(0, self.extra_args[0] + ["-walletbroadcast=0"])
        self.start_node(1, self.extra_args[1] + ["-walletbroadcast=0"])
        self.start_node(2, self.extra_args[2] + ["-walletbroadcast=0"])
        connect_nodes(self.nodes[0], self.nodes[1])
        connect_nodes(self.nodes[1], self.nodes[2])
        connect_nodes(self.nodes[0], self.nodes[2])
        self.sync_all(self.nodes[0:3])

        txid_not_broadcast = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), Decimal('20'))
        tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)
        self.nodes[1].generate(1)  # mine a block, tx should not be in there
        self.sync_all(self.nodes[0:3])
        # should not be changed because tx was not broadcasted
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        # now broadcast from another node, mine a block, sync, and check the
        # balance
        self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex'])
        self.nodes[1].generate(1)
        self.sync_all(self.nodes[0:3])
        node_2_bal += Decimal('20')
        tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        # create another tx
        txid_not_broadcast = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), Decimal('20'))

        # restart the nodes with -walletbroadcast=1
        self.stop_nodes()
        self.start_node(0, self.extra_args[0])
        self.start_node(1, self.extra_args[1])
        self.start_node(2, self.extra_args[2])
        connect_nodes(self.nodes[0], self.nodes[1])
        connect_nodes(self.nodes[1], self.nodes[2])
        connect_nodes(self.nodes[0], self.nodes[2])
        self.sync_blocks(self.nodes[0:3])

        self.nodes[0].generate(1)
        self.sync_blocks(self.nodes[0:3])
        node_2_bal += Decimal('20')

        # tx should be added to balance because after restarting the nodes tx
        # should be broadcasted
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        # send a tx with value in a string (PR#6380 +)
        txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "200")
        tx_obj = self.nodes[0].gettransaction(txid)
        assert_equal(tx_obj['amount'], Decimal('-200'))

        txid = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), "0.01")
        tx_obj = self.nodes[0].gettransaction(txid)
        assert_equal(tx_obj['amount'], Decimal('-0.01'))

        # check if JSON parser can handle scientific notation in strings
        txid = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), "1e-2")
        tx_obj = self.nodes[0].gettransaction(txid)
        assert_equal(tx_obj['amount'], Decimal('-0.01'))

        # General checks for errors from incorrect inputs
        # This will raise an exception because the amount type is wrong
        assert_raises_rpc_error(-3, "Invalid amount",
                                self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-2")

        # This will raise an exception since generate does not accept a string
        assert_raises_rpc_error(-1, "not an integer",
                                self.nodes[0].generate, "200")

        # This will raise an exception for the invalid private key format
        assert_raises_rpc_error(-5,
                                "Invalid private key encoding",
                                self.nodes[0].importprivkey,
                                "invalid")

        # This will raise an exception for importing an address with the PS2H
        # flag
        temp_address = self.nodes[1].getnewaddress()
        assert_raises_rpc_error(-5,
                                "Cannot use the p2sh flag with an address - use a script instead",
                                self.nodes[0].importaddress,
                                temp_address,
                                "label",
                                False,
                                True)

        # This will raise an exception for attempting to dump the private key
        # of an address you do not own
        assert_raises_rpc_error(-4,
                                "Private key for address",
                                self.nodes[0].dumpprivkey,
                                temp_address)

        # This will raise an exception for attempting to get the private key of
        # an invalid Bitcoin address
        assert_raises_rpc_error(-5,
                                "Invalid Bitcoin address",
                                self.nodes[0].dumpprivkey,
                                "invalid")

        # This will raise an exception for attempting to set a label for an
        # invalid Bitcoin address
        assert_raises_rpc_error(-5,
                                "Invalid Bitcoin address",
                                self.nodes[0].setlabel,
                                "invalid address",
                                "label")

        # This will raise an exception for importing an invalid address
        assert_raises_rpc_error(-5,
                                "Invalid Bitcoin address or script",
                                self.nodes[0].importaddress,
                                "invalid")

        # This will raise an exception for attempting to import a pubkey that
        # isn't in hex
        assert_raises_rpc_error(-5,
                                "Pubkey must be a hex string",
                                self.nodes[0].importpubkey,
                                "not hex")

        # This will raise an exception for importing an invalid pubkey
        assert_raises_rpc_error(-5,
                                "Pubkey is not a valid public key",
                                self.nodes[0].importpubkey,
                                "5361746f736869204e616b616d6f746f")

        # Import address and private key to check correct behavior of spendable unspents
        # 1. Send some coins to generate new UTXO
        address_to_import = self.nodes[2].getnewaddress()
        txid = self.nodes[0].sendtoaddress(address_to_import, 1)
        self.nodes[0].generate(1)
        self.sync_all(self.nodes[0:3])

        # 2. Import address from node2 to node1
        self.nodes[1].importaddress(address_to_import)

        # 3. Validate that the imported address is watch-only on node1
        assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"]

        # 4. Check that the unspents after import are not spendable
        assert_array_result(self.nodes[1].listunspent(),
                            {"address": address_to_import},
                            {"spendable": False})

        # 5. Import private key of the previously imported address on node1
        priv_key = self.nodes[2].dumpprivkey(address_to_import)
        self.nodes[1].importprivkey(priv_key)

        # 6. Check that the unspents are now spendable on node1
        assert_array_result(self.nodes[1].listunspent(),
                            {"address": address_to_import},
                            {"spendable": True})

        # Mine a block from node0 to an address from node1
        coinbase_addr = self.nodes[1].getnewaddress()
        block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0]
        coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0]
        self.sync_all(self.nodes[0:3])

        # Check that the txid and balance is found by node1
        self.nodes[1].gettransaction(coinbase_txid)

        # check if wallet or blockchain maintenance changes the balance
        self.sync_all(self.nodes[0:3])
        blocks = self.nodes[0].generate(2)
        self.sync_all(self.nodes[0:3])
        balance_nodes = [self.nodes[i].getbalance() for i in range(3)]
        block_count = self.nodes[0].getblockcount()

        # Check modes:
        #   - True: unicode escaped as \u....
        #   - False: unicode directly as UTF-8
        for mode in [True, False]:
            self.nodes[0].rpc.ensure_ascii = mode
            # unicode check: Basic Multilingual Plane, Supplementary Plane
            # respectively
            for label in [u'б€б‹аБаА', u'№…Ё']:
                addr = self.nodes[0].getnewaddress()
                self.nodes[0].setlabel(addr, label)
                test_address(self.nodes[0], addr, labels=[label])
                assert label in self.nodes[0].listlabels()
        # restore to default
        self.nodes[0].rpc.ensure_ascii = True

        # maintenance tests
        maintenance = [
            '-rescan',
            '-reindex',
            '-zapwallettxes=1',
            '-zapwallettxes=2',
        ]
        chainlimit = 6
        for m in maintenance:
            self.log.info("check " + m)
            self.stop_nodes()
            # set lower ancestor limit for later
            self.start_node(
                0, self.extra_args[0] + [m, "-limitancestorcount=" + str(chainlimit)])
            self.start_node(
                1, self.extra_args[1] + [m, "-limitancestorcount=" + str(chainlimit)])
            self.start_node(
                2, self.extra_args[2] + [m, "-limitancestorcount=" + str(chainlimit)])
            if m == '-reindex':
                # reindex will leave rpc warm up "early"; Wait for it to finish
                wait_until(lambda: [block_count] * 3 ==
                           [self.nodes[i].getblockcount() for i in range(3)])
            assert_equal(balance_nodes, [
                         self.nodes[i].getbalance() for i in range(3)])

        # Exercise listsinceblock with the last two blocks
        coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])
        assert_equal(coinbase_tx_1["lastblock"], blocks[1])
        assert_equal(len(coinbase_tx_1["transactions"]), 1)
        assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])
        assert_equal(len(self.nodes[0].listsinceblock(
            blocks[1])["transactions"]), 0)

        # ==Check that wallet prefers to use coins that don't exceed mempool li

        # Get all non-zero utxos together
        chain_addrs = [self.nodes[0].getnewaddress(
        ), self.nodes[0].getnewaddress()]
        singletxid = self.nodes[0].sendtoaddress(
            chain_addrs[0], self.nodes[0].getbalance(), "", "", True)
        self.nodes[0].generate(1)
        node0_balance = self.nodes[0].getbalance()
        # Split into two chains
        rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {
                                                   chain_addrs[0]: node0_balance / 2 - Decimal('1'), chain_addrs[1]: node0_balance / 2 - Decimal('1')})
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        singletxid = self.nodes[0].sendrawtransaction(
            hexstring=signedtx["hex"], maxfeerate=0)
        self.nodes[0].generate(1)

        # Make a long chain of unconfirmed payments without hitting mempool limit
        # Each tx we make leaves only one output of change on a chain 1 longer
        # Since the amount to send is always much less than the outputs, we only ever need one output
        # So we should be able to generate exactly chainlimit txs for each
        # original output
        sending_addr = self.nodes[1].getnewaddress()
        txid_list = []
        for i in range(chainlimit * 2):
            txid_list.append(self.nodes[0].sendtoaddress(
                sending_addr, Decimal('0.01')))
        assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2)
        assert_equal(len(txid_list), chainlimit * 2)

        # Without walletrejectlongchains, we will still generate a txid
        # The tx will be stored in the wallet but not accepted to the mempool
        extra_txid = self.nodes[0].sendtoaddress(
            sending_addr, Decimal('0.01'))
        assert extra_txid not in self.nodes[0].getrawmempool()
        assert extra_txid in [tx["txid"]
                              for tx in self.nodes[0].listtransactions()]
        self.nodes[0].abandontransaction(extra_txid)
        total_txs = len(self.nodes[0].listtransactions("*", 99999))

        # Try with walletrejectlongchains
        # Double chain limit but require combining inputs, so we pass
        # SelectCoinsMinConf
        self.stop_node(0)
        self.start_node(0,
                        self.extra_args[0] + ["-walletrejectlongchains",
                                              "-limitancestorcount=" + str(2 * chainlimit)])

        # wait until the wallet has submitted all transactions to the mempool
        wait_until(
            lambda: len(
                self.nodes[0].getrawmempool()) == chainlimit *
            2)

        node0_balance = self.nodes[0].getbalance()
        # With walletrejectlongchains we will not create the tx and store it in
        # our wallet.
        assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain",
                                self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('1'))

        # Verify nothing new in wallet
        assert_equal(total_txs, len(
            self.nodes[0].listtransactions("*", 99999)))

        # Test getaddressinfo on external address. Note that these addresses
        # are taken from disablewallet.py
        assert_raises_rpc_error(-5, "Invalid address",
                                self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy")
        address_info = self.nodes[0].getaddressinfo(
            "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ")
        assert_equal(address_info['address'],
                     "bchreg:qp8rs4qyd3aazk22eyzwg7fmdfzmxm02pywavdajx4")
        assert_equal(address_info["scriptPubKey"],
                     "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac")
        assert not address_info["ismine"]
        assert not address_info["iswatchonly"]
        assert not address_info["isscript"]
        assert not address_info["ischange"]

        # Test getaddressinfo 'ischange' field on change address.
        self.nodes[0].generate(1)
        destination = self.nodes[1].getnewaddress()
        txid = self.nodes[0].sendtoaddress(destination, 0.123)
        tx = self.nodes[0].decoderawtransaction(
            self.nodes[0].gettransaction(txid)['hex'])
        output_addresses = [vout['scriptPubKey']['addresses'][0]
                            for vout in tx["vout"]]
        assert len(output_addresses) > 1
        for address in output_addresses:
            ischange = self.nodes[0].getaddressinfo(address)['ischange']
            assert_equal(ischange, address != destination)
            if ischange:
                change = address
        self.nodes[0].setlabel(change, 'foobar')
        assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)

        # Test gettransaction response with different arguments.
        self.log.info(
            "Testing gettransaction response with different arguments...")
        self.nodes[0].setlabel(change, 'baz')
        baz = self.nodes[0].listtransactions(label="baz", count=1)[0]
        expected_receive_vout = {"label": "baz",
                                 "address": baz["address"],
                                 "amount": baz["amount"],
                                 "category": baz["category"],
                                 "vout": baz["vout"]}
        expected_fields = frozenset({'amount',
                                     'confirmations',
                                     'details',
                                     'fee',
                                     'hex',
                                     'time',
                                     'timereceived',
                                     'trusted',
                                     'txid',
                                     'walletconflicts'})
        verbose_field = "decoded"
        expected_verbose_fields = expected_fields | {verbose_field}

        self.log.debug("Testing gettransaction response without verbose")
        tx = self.nodes[0].gettransaction(txid=txid)
        assert_equal(set([*tx]), expected_fields)
        assert_array_result(
            tx["details"], {
                "category": "receive"}, expected_receive_vout)

        self.log.debug(
            "Testing gettransaction response with verbose set to False")
        tx = self.nodes[0].gettransaction(txid=txid, verbose=False)
        assert_equal(set([*tx]), expected_fields)
        assert_array_result(
            tx["details"], {
                "category": "receive"}, expected_receive_vout)

        self.log.debug(
            "Testing gettransaction response with verbose set to True")
        tx = self.nodes[0].gettransaction(txid=txid, verbose=True)
        assert_equal(set([*tx]), expected_verbose_fields)
        assert_array_result(
            tx["details"], {
                "category": "receive"}, expected_receive_vout)
        assert_equal(
            tx[verbose_field],
            self.nodes[0].decoderawtransaction(
                tx["hex"]))
    def run_test(self):
        min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
        # This test is not meant to test fee estimation and we'd like
        # to be sure all txs are sent at a consistent desired feerate
        for node in self.nodes:
            node.settxfee(min_relay_tx_fee)

        # if the fee's positive delta is higher than this value tests will fail,
        # neg. delta always fail the tests.
        # The size of the signature of every input may be at most 2 bytes larger
        # than a minimum sized signature.

        #            = 2 bytes * minRelayTxFeePerByte
        feeTolerance = 2 * min_relay_tx_fee/1000

        self.nodes[2].generate(1)
        self.sync_all()
        self.nodes[0].generate(121)
        self.sync_all()

        # ensure that setting changePosition in fundraw with an exact match is handled properly
        rawmatch = self.nodes[2].createrawtransaction([], {self.nodes[2].getnewaddress():50})
        rawmatch = self.nodes[2].fundrawtransaction(rawmatch, {"changePosition":1, "subtractFeeFromOutputs":[0]})
        assert_equal(rawmatch["changepos"], -1)

        watchonly_address = self.nodes[0].getnewaddress()
        watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
        watchonly_amount = Decimal(200)
        self.nodes[3].importpubkey(watchonly_pubkey, "", True)
        watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount)

        # Lock UTXO so nodes[0] doesn't accidentally spend it
        watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid, watchonly_address)
        self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])

        self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10)

        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.nodes[0].generate(1)
        self.sync_all()

        ###############
        # simple test #
        ###############
        inputs  = [ ]
        outputs = { self.nodes[0].getnewaddress() : 1.0 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert(len(dec_tx['vin']) > 0) #test that we have enough inputs

        ##############################
        # simple test with two coins #
        ##############################
        inputs  = [ ]
        outputs = { self.nodes[0].getnewaddress() : 2.2 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert(len(dec_tx['vin']) > 0) #test if we have enough inputs

        ##############################
        # simple test with two coins #
        ##############################
        inputs  = [ ]
        outputs = { self.nodes[0].getnewaddress() : 2.6 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert(len(dec_tx['vin']) > 0)
        assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')


        ################################
        # simple test with two outputs #
        ################################
        inputs  = [ ]
        outputs = { self.nodes[0].getnewaddress() : 2.6, self.nodes[1].getnewaddress() : 2.5 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert(len(dec_tx['vin']) > 0)
        assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')


        #########################################################################
        # test a fundrawtransaction with a VIN greater than the required amount #
        #########################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
        outputs = { self.nodes[0].getnewaddress() : 1.0 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee


        #####################################################################
        # test a fundrawtransaction with which will not get a change output #
        #####################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
        outputs = { self.nodes[0].getnewaddress() : Decimal(5.0) - fee - feeTolerance }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert_equal(rawtxfund['changepos'], -1)
        assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee


        ####################################################
        # test a fundrawtransaction with an invalid option #
        ####################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
        outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'})

        # reserveChangeKey was deprecated and is now removed
        assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True}))

        ############################################################
        # test a fundrawtransaction with an invalid change address #
        ############################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
        outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'})

        ############################################################
        # test a fundrawtransaction with a provided change address #
        ############################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
        outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        change = self.nodes[2].getnewaddress()
        assert_raises_rpc_error(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2})
        rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0})
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        out = dec_tx['vout'][0]
        assert_equal(change, out['scriptPubKey']['addresses'][0])

        #########################################################
        # test a fundrawtransaction with a provided change type #
        #########################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
        outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
        assert_raises_rpc_error(-5, "Unknown change type ''", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
        rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'})
        dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
        assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])

        #########################################################################
        # test a fundrawtransaction with a VIN smaller than the required amount #
        #########################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']}]
        outputs = { self.nodes[0].getnewaddress() : 1.0 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)

        # 4-byte version + 1-byte vin count + 36-byte prevout then script_len
        rawtx = rawtx[:82] + "0100" + rawtx[84:]

        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
        assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for i, out in enumerate(dec_tx['vout']):
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts+=1
            else:
                assert_equal(i, rawtxfund['changepos'])

        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
        assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])

        assert_equal(matchingOuts, 1)
        assert_equal(len(dec_tx['vout']), 2)


        ###########################################
        # test a fundrawtransaction with two VINs #
        ###########################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)
        utx2 = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']},{'txid' : utx2['txid'], 'vout' : utx2['vout']} ]
        outputs = { self.nodes[0].getnewaddress() : 6.0 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for out in dec_tx['vout']:
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts+=1

        assert_equal(matchingOuts, 1)
        assert_equal(len(dec_tx['vout']), 2)

        matchingIns = 0
        for vinOut in dec_tx['vin']:
            for vinIn in inputs:
                if vinIn['txid'] == vinOut['txid']:
                    matchingIns+=1

        assert_equal(matchingIns, 2) #we now must see two vins identical to vins given as params

        #########################################################
        # test a fundrawtransaction with two VINs and two vOUTs #
        #########################################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)
        utx2 = get_unspent(self.nodes[2].listunspent(), 5)

        inputs  = [ {'txid' : utx['txid'], 'vout' : utx['vout']},{'txid' : utx2['txid'], 'vout' : utx2['vout']} ]
        outputs = { self.nodes[0].getnewaddress() : 6.0, self.nodes[0].getnewaddress() : 1.0 }
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for out in dec_tx['vout']:
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts+=1

        assert_equal(matchingOuts, 2)
        assert_equal(len(dec_tx['vout']), 3)

        ##############################################
        # test a fundrawtransaction with invalid vin #
        ##############################################
        inputs  = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin!
        outputs = { self.nodes[0].getnewaddress() : 1.0}
        rawtx   = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)

        assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx)

        ############################################################
        #compare fee of a standard pubkeyhash transaction
        inputs = []
        outputs = {self.nodes[1].getnewaddress():1.1}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert(feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a standard pubkeyhash transaction with multiple outputs
        inputs = []
        outputs = {self.nodes[1].getnewaddress():1.1,self.nodes[1].getnewaddress():1.2,self.nodes[1].getnewaddress():0.1,self.nodes[1].getnewaddress():1.3,self.nodes[1].getnewaddress():0.2,self.nodes[1].getnewaddress():0.3}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)
        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendmany("", outputs)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert(feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################


        ############################################################
        #compare fee of a 2of2 multisig p2sh transaction

        # create 2of2 addr
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[1].getnewaddress()

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

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

        inputs = []
        outputs = {mSigObj:1.1}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert(feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################


        ############################################################
        #compare fee of a standard pubkeyhash transaction

        # create 4of5 addr
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[1].getnewaddress()
        addr3 = self.nodes[1].getnewaddress()
        addr4 = self.nodes[1].getnewaddress()
        addr5 = self.nodes[1].getnewaddress()

        addr1Obj = self.nodes[1].getaddressinfo(addr1)
        addr2Obj = self.nodes[1].getaddressinfo(addr2)
        addr3Obj = self.nodes[1].getaddressinfo(addr3)
        addr4Obj = self.nodes[1].getaddressinfo(addr4)
        addr5Obj = self.nodes[1].getaddressinfo(addr5)

        mSigObj = self.nodes[1].addmultisigaddress(4, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'], addr4Obj['pubkey'], addr5Obj['pubkey']])['address']

        inputs = []
        outputs = {mSigObj:1.1}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert(feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################


        ############################################################
        # spend a 2of2 multisig transaction over fundraw

        # create 2of2 addr
        addr1 = self.nodes[2].getnewaddress()
        addr2 = self.nodes[2].getnewaddress()

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

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


        # send 1.2 BTC to msig addr
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        oldBalance = self.nodes[1].getbalance()
        inputs = []
        outputs = {self.nodes[1].getnewaddress():1.1}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[2].fundrawtransaction(rawtx)

        signedTx = self.nodes[2].signrawtransactionwithwallet(fundedTx['hex'])
        txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        # make sure funds are received at node1
        assert_equal(oldBalance+Decimal('1.10000000'), self.nodes[1].getbalance())

        ############################################################
        # locked wallet test
        self.nodes[1].encryptwallet("test")
        self.stop_nodes()

        self.start_nodes()
        # This test is not meant to test fee estimation and we'd like
        # to be sure all txs are sent at a consistent desired feerate
        for node in self.nodes:
            node.settxfee(min_relay_tx_fee)

        connect_nodes_bi(self.nodes,0,1)
        connect_nodes_bi(self.nodes,1,2)
        connect_nodes_bi(self.nodes,0,2)
        connect_nodes_bi(self.nodes,0,3)
        # Again lock the watchonly UTXO or nodes[0] may spend it, because
        # lockunspent is memory-only and thus lost on restart
        self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}])
        self.sync_all()

        # drain the keypool
        self.nodes[1].getnewaddress()
        self.nodes[1].getrawchangeaddress()
        inputs = []
        outputs = {self.nodes[0].getnewaddress():1.1}
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        # fund a transaction that requires a new key for the change output
        # creating the key must be impossible because the wallet is locked
        assert_raises_rpc_error(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx)

        #refill the keypool
        self.nodes[1].walletpassphrase("test", 100)
        self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address
        self.nodes[1].walletlock()

        assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)

        oldBalance = self.nodes[0].getbalance()

        inputs = []
        outputs = {self.nodes[0].getnewaddress():1.1}
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawtx)

        #now we need to unlock
        self.nodes[1].walletpassphrase("test", 600)
        signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
        txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
        self.nodes[1].generate(1)
        self.sync_all()

        # make sure funds are received at node1
        assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance())


        ###############################################
        # multiple (~19) inputs tx test | Compare fee #
        ###############################################

        #empty node1, send some small coins from node0 to node1
        self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        for i in range(0,20):
            self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
        self.nodes[0].generate(1)
        self.sync_all()

        #fund a tx with ~20 small inputs
        inputs = []
        outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawtx)

        #create same transaction over sendtoaddress
        txId = self.nodes[1].sendmany("", outputs)
        signedFee = self.nodes[1].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert(feeDelta >= 0 and feeDelta <= feeTolerance*19) #~19 inputs


        #############################################
        # multiple (~19) inputs tx test | sign/send #
        #############################################

        #again, empty node1, send some small coins from node0 to node1
        self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        for i in range(0,20):
            self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
        self.nodes[0].generate(1)
        self.sync_all()

        #fund a tx with ~20 small inputs
        oldBalance = self.nodes[0].getbalance()

        inputs = []
        outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
        rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawtx)
        fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
        txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(oldBalance+Decimal('50.19000000'), self.nodes[0].getbalance()) #0.19+block reward

        #####################################################
        # test fundrawtransaction with OP_RETURN and no vin #
        #####################################################

        rawtx   = "0100000000010000000000000000066a047465737400000000"
        dec_tx  = self.nodes[2].decoderawtransaction(rawtx)

        assert_equal(len(dec_tx['vin']), 0)
        assert_equal(len(dec_tx['vout']), 1)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        dec_tx  = self.nodes[2].decoderawtransaction(rawtxfund['hex'])

        assert_greater_than(len(dec_tx['vin']), 0) # at least one vin
        assert_equal(len(dec_tx['vout']), 2) # one change output added


        ##################################################
        # test a fundrawtransaction using only watchonly #
        ##################################################

        inputs = []
        outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = self.nodes[3].fundrawtransaction(rawtx, {'includeWatching': True })
        res_dec = self.nodes[0].decoderawtransaction(result["hex"])
        assert_equal(len(res_dec["vin"]), 1)
        assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)

        assert("fee" in result.keys())
        assert_greater_than(result["changepos"], -1)

        ###############################################################
        # test fundrawtransaction using the entirety of watched funds #
        ###############################################################

        inputs = []
        outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        # Backward compatibility test (2nd param is includeWatching)
        result = self.nodes[3].fundrawtransaction(rawtx, True)
        res_dec = self.nodes[0].decoderawtransaction(result["hex"])
        assert_equal(len(res_dec["vin"]), 2)
        assert(res_dec["vin"][0]["txid"] == watchonly_txid or res_dec["vin"][1]["txid"] == watchonly_txid)

        assert_greater_than(result["fee"], 0)
        assert_greater_than(result["changepos"], -1)
        assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10)

        signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"])
        assert(not signedtx["complete"])
        signedtx = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"])
        assert(signedtx["complete"])
        self.nodes[0].sendrawtransaction(signedtx["hex"])
        self.nodes[0].generate(1)
        self.sync_all()

        #######################
        # Test feeRate option #
        #######################

        # Make sure there is exactly one input so coin selection can't skew the result
        assert_equal(len(self.nodes[3].listunspent(1)), 1)

        inputs = []
        outputs = {self.nodes[3].getnewaddress() : 1}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
        result = self.nodes[3].fundrawtransaction(rawtx) # uses min_relay_tx_fee (set by settxfee)
        result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee})
        result3 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 10*min_relay_tx_fee})
        result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
        assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
        assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)

        ################################
        # Test no address reuse occurs #
        ################################

        result3 = self.nodes[3].fundrawtransaction(rawtx)
        res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
        changeaddress = ""
        for out in res_dec['vout']:
            if out['value'] > 1.0:
                changeaddress += out['scriptPubKey']['addresses'][0]
        assert(changeaddress != "")
        nextaddr = self.nodes[3].getnewaddress()
        # Now the change address key should be removed from the keypool
        assert(changeaddress != nextaddr)

        ######################################
        # Test subtractFeeFromOutputs option #
        ######################################

        # Make sure there is exactly one input so coin selection can't skew the result
        assert_equal(len(self.nodes[3].listunspent(1)), 1)

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): 1}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee)
                  self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list
                  self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses min_relay_tx_fee (set by settxfee)
                  self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}),
                  self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee, "subtractFeeFromOutputs": [0]})]

        dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) for tx_ in result]
        output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)]
        change = [d['vout'][r['changepos']]['value'] for d, r in zip(dec_tx, result)]

        assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee'])
        assert_equal(result[3]['fee'], result[4]['fee'])
        assert_equal(change[0], change[1])
        assert_equal(output[0], output[1])
        assert_equal(output[0], output[2] + result[2]['fee'])
        assert_equal(change[0] + result[0]['fee'], change[2])
        assert_equal(output[3], output[4] + result[4]['fee'])
        assert_equal(change[3] + result[3]['fee'], change[4])

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): value for value in (1.0, 1.1, 1.2, 1.3)}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = [self.nodes[3].fundrawtransaction(rawtx),
                  # split the fee between outputs 0, 2, and 3, but not output 1
                  self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0, 2, 3]})]

        dec_tx = [self.nodes[3].decoderawtransaction(result[0]['hex']),
                  self.nodes[3].decoderawtransaction(result[1]['hex'])]

        # Nested list of non-change output amounts for each transaction
        output = [[out['value'] for i, out in enumerate(d['vout']) if i != r['changepos']]
                  for d, r in zip(dec_tx, result)]

        # List of differences in output amounts between normal and subtractFee transactions
        share = [o0 - o1 for o0, o1 in zip(output[0], output[1])]

        # output 1 is the same in both transactions
        assert_equal(share[1], 0)

        # the other 3 outputs are smaller as a result of subtractFeeFromOutputs
        assert_greater_than(share[0], 0)
        assert_greater_than(share[2], 0)
        assert_greater_than(share[3], 0)

        # outputs 2 and 3 take the same share of the fee
        assert_equal(share[2], share[3])

        # output 0 takes at least as much share of the fee, and no more than 2 satoshis more, than outputs 2 and 3
        assert_greater_than_or_equal(share[0], share[2])
        assert_greater_than_or_equal(share[2] + Decimal(2e-8), share[0])

        # the fee is the same in both transactions
        assert_equal(result[0]['fee'], result[1]['fee'])

        # the total subtracted from the outputs is equal to the fee
        assert_equal(share[0] + share[2] + share[3], result[0]['fee'])
    def run_test(self):
        # Check that there's no UTXO on none of the nodes
        assert_equal(len(self.nodes[0].listunspent()), 0)
        assert_equal(len(self.nodes[1].listunspent()), 0)
        assert_equal(len(self.nodes[2].listunspent()), 0)

        self.log.info("Mining blocks...")

        self.nodes[0].generate(1)

        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], 50)
        assert_equal(walletinfo['balance'], 0)

        self.sync_all([self.nodes[0:3]])
        self.nodes[1].generate(101)
        self.sync_all([self.nodes[0:3]])

        assert_equal(self.nodes[0].getbalance(), 50)
        assert_equal(self.nodes[1].getbalance(), 50)
        assert_equal(self.nodes[2].getbalance(), 0)

        # Check getbalance with different arguments
        assert_equal(self.nodes[0].getbalance("*"), 50)
        assert_equal(self.nodes[0].getbalance("*", 1), 50)
        assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
        assert_equal(self.nodes[0].getbalance(minconf=1), 50)

        # first argument of getbalance must be excluded or set to "*"
        assert_raises_rpc_error(-32, "dummy first argument must be excluded or set to \"*\"",
                                self.nodes[0].getbalance, "")

        # Check that only first and second nodes have UTXOs
        utxos = self.nodes[0].listunspent()
        assert_equal(len(utxos), 1)
        assert_equal(len(self.nodes[1].listunspent()), 1)
        assert_equal(len(self.nodes[2].listunspent()), 0)

        self.log.info("test gettxout")
        confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"]
        # First, outputs that are unspent both in the chain and in the
        # mempool should appear with or without include_mempool
        txout = self.nodes[0].gettxout(
            txid=confirmed_txid, n=confirmed_index, include_mempool=False)
        assert_equal(txout['value'], 50)
        txout = self.nodes[0].gettxout(
            txid=confirmed_txid, n=confirmed_index, include_mempool=True)
        assert_equal(txout['value'], 50)

        # Send 21 BCH from 0 to 2 using sendtoaddress call.
        # Locked memory should use at least 32 bytes to sign each transaction
        self.log.info("test getmemoryinfo")
        memory_before = self.nodes[0].getmemoryinfo()
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
        mempool_txid = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), 10)
        memory_after = self.nodes[0].getmemoryinfo()
        assert memory_before['locked']['used'] + \
            64 <= memory_after['locked']['used']

        self.log.info("test gettxout (second part)")
        # utxo spent in mempool should be visible if you exclude mempool
        # but invisible if you include mempool
        txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False)
        assert_equal(txout['value'], 50)
        txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True)
        assert txout is None
        # new utxo from mempool should be invisible if you exclude mempool
        # but visible if you include mempool
        txout = self.nodes[0].gettxout(mempool_txid, 0, False)
        assert txout is None
        txout1 = self.nodes[0].gettxout(mempool_txid, 0, True)
        txout2 = self.nodes[0].gettxout(mempool_txid, 1, True)
        # note the mempool tx will have randomly assigned indices
        # but 10 will go to node2 and the rest will go to node0
        balance = self.nodes[0].getbalance()
        assert_equal(set([txout1['value'], txout2['value']]),
                     set([10, balance]))
        walletinfo = self.nodes[0].getwalletinfo()
        assert_equal(walletinfo['immature_balance'], 0)

        # Have node0 mine a block, thus it will collect its own fee.
        self.nodes[0].generate(1)
        self.sync_all([self.nodes[0:3]])

        # Exercise locking of unspent outputs
        unspent_0 = self.nodes[2].listunspent()[0]
        unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
        assert_raises_rpc_error(-8, "Invalid parameter, expected locked output",
                                self.nodes[2].lockunspent, True, [unspent_0])
        self.nodes[2].lockunspent(False, [unspent_0])
        assert_raises_rpc_error(-8, "Invalid parameter, output already locked",
                                self.nodes[2].lockunspent, False, [unspent_0])
        assert_raises_rpc_error(-4, "Insufficient funds",
                                self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
        assert_equal([unspent_0], self.nodes[2].listlockunspent())
        self.nodes[2].lockunspent(True, [unspent_0])
        assert_equal(len(self.nodes[2].listlockunspent()), 0)
        assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')",
                                self.nodes[2].lockunspent, False,
                                [{"txid": "0000000000000000000000000000000000", "vout": 0}])
        assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')",
                                self.nodes[2].lockunspent, False,
                                [{"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", "vout": 0}])
        assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
                                self.nodes[2].lockunspent, False,
                                [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0}])
        assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds",
                                self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}])

        # Have node1 generate 100 blocks (so node0 can recover the fee)
        self.nodes[1].generate(100)
        self.sync_all([self.nodes[0:3]])

        # node0 should end up with 100 btc in block rewards plus fees, but
        # minus the 21 plus fees sent to node2
        assert_equal(self.nodes[0].getbalance(), 100 - 21)
        assert_equal(self.nodes[2].getbalance(), 21)

        # Node0 should have two unspent outputs.
        # Create a couple of transactions to send them to node2, submit them through
        # node1, and make sure both node0 and node2 pick them up properly:
        node0utxos = self.nodes[0].listunspent(1)
        assert_equal(len(node0utxos), 2)

        # create both transactions
        txns_to_send = []
        for utxo in node0utxos:
            inputs = []
            outputs = {}
            inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]})
            outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3
            raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
            txns_to_send.append(
                self.nodes[0].signrawtransactionwithwallet(raw_tx))

        # Have node 1 (miner) send the transactions
        self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True)
        self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], True)

        # Have node1 mine a block to confirm transactions:
        self.nodes[1].generate(1)
        self.sync_all([self.nodes[0:3]])

        assert_equal(self.nodes[0].getbalance(), 0)
        assert_equal(self.nodes[2].getbalance(), 94)

        # Verify that a spent output cannot be locked anymore
        spent_0 = {"txid": node0utxos[0]["txid"],
                   "vout": node0utxos[0]["vout"]}
        assert_raises_rpc_error(-8, "Invalid parameter, expected unspent output",
                                self.nodes[0].lockunspent, False, [spent_0])

        # Send 10 BCH normal
        old_balance = self.nodes[2].getbalance()
        address = self.nodes[0].getnewaddress("test")
        fee_per_byte = Decimal('0.001') / 1000
        self.nodes[2].settxfee(fee_per_byte * 1000)
        txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        ctx = FromHex(CTransaction(), self.nodes[2].getrawtransaction(txid))
        node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), old_balance - Decimal('10'),
                                           fee_per_byte, ctx.billable_size())
        assert_equal(self.nodes[0].getbalance(), Decimal('10'))

        # Send 10 BCH with subtract fee from amount
        txid = self.nodes[2].sendtoaddress(address, 10, "", "", True)
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal -= Decimal('10')
        assert_equal(self.nodes[2].getbalance(), node_2_bal)
        node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal(
            '20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))

        # Sendmany 10 BCH
        txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [])
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_0_bal += Decimal('10')
        ctx = FromHex(CTransaction(), self.nodes[2].getrawtransaction(txid))
        node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(
        ), node_2_bal - Decimal('10'), fee_per_byte, ctx.billable_size())
        assert_equal(self.nodes[0].getbalance(), node_0_bal)

        # Sendmany 10 BCH with subtract fee from amount
        txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [address])
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal -= Decimal('10')
        assert_equal(self.nodes[2].getbalance(), node_2_bal)
        ctx = FromHex(CTransaction(), self.nodes[2].getrawtransaction(txid))
        node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(
        ), node_0_bal + Decimal('10'), fee_per_byte, ctx.billable_size())

        # Test ResendWalletTransactions:
        # Create a couple of transactions, then start up a fourth
        # node (nodes[3]) and ask nodes[0] to rebroadcast.
        # EXPECT: nodes[3] should have those transactions in its mempool.
        txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
        txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
        sync_mempools(self.nodes[0:2])

        self.start_node(3)
        connect_nodes_bi(self.nodes[0], self.nodes[3])
        sync_blocks(self.nodes)

        relayed = self.nodes[0].resendwallettransactions()
        assert_equal(set(relayed), {txid1, txid2})
        sync_mempools(self.nodes)

        assert txid1 in self.nodes[3].getrawmempool()

        # Exercise balance rpcs
        assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1)
        assert_equal(self.nodes[0].getunconfirmedbalance(), 1)

        # check if we can list zero value tx as available coins
        # 1. create rawtx
        # 2. hex-changed one output to 0.0
        # 3. sign and send
        # 4. check if recipient (node0) can list the zero value tx
        usp = self.nodes[1].listunspent()
        inputs = [{"txid": usp[0]['txid'], "vout":usp[0]['vout']}]
        outputs = {self.nodes[1].getnewaddress(): 49.998,
                   self.nodes[0].getnewaddress(): 11.11}

        rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace(
            "c0833842", "00000000")  # replace 11.11 with 0.0 (int32)
        decRawTx = self.nodes[1].decoderawtransaction(rawTx)
        signedRawTx = self.nodes[1].signrawtransactionwithwallet(rawTx)
        decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex'])
        zeroValueTxid = decRawTx['txid']
        self.nodes[1].sendrawtransaction(signedRawTx['hex'])

        self.sync_all()
        self.nodes[1].generate(1)  # mine a block
        self.sync_all()

        # zero value tx must be in listunspents output
        unspentTxs = self.nodes[0].listunspent()
        found = False
        for uTx in unspentTxs:
            if uTx['txid'] == zeroValueTxid:
                found = True
                assert_equal(uTx['amount'], Decimal('0'))
        assert found

        # do some -walletbroadcast tests
        self.stop_nodes()
        self.start_node(0, ["-walletbroadcast=0"])
        self.start_node(1, ["-walletbroadcast=0"])
        self.start_node(2, ["-walletbroadcast=0"])
        connect_nodes_bi(self.nodes[0], self.nodes[1])
        connect_nodes_bi(self.nodes[1], self.nodes[2])
        connect_nodes_bi(self.nodes[0], self.nodes[2])
        self.sync_all([self.nodes[0:3]])

        txIdNotBroadcasted = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), 2)
        txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
        self.nodes[1].generate(1)  # mine a block, tx should not be in there
        self.sync_all([self.nodes[0:3]])
        # should not be changed because tx was not broadcasted
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        # now broadcast from another node, mine a block, sync, and check the
        # balance
        self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex'])
        self.nodes[1].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal += 2
        txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        # create another tx
        txIdNotBroadcasted = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), 2)

        # restart the nodes with -walletbroadcast=1
        self.stop_nodes()
        self.start_node(0)
        self.start_node(1)
        self.start_node(2)
        connect_nodes_bi(self.nodes[0], self.nodes[1])
        connect_nodes_bi(self.nodes[1], self.nodes[2])
        connect_nodes_bi(self.nodes[0], self.nodes[2])
        sync_blocks(self.nodes[0:3])

        self.nodes[0].generate(1)
        sync_blocks(self.nodes[0:3])
        node_2_bal += 2

        # tx should be added to balance because after restarting the nodes tx
        # should be broadcasted
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        # send a tx with value in a string (PR#6380 +)
        txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
        txObj = self.nodes[0].gettransaction(txId)
        assert_equal(txObj['amount'], Decimal('-2'))

        txId = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), "0.0001")
        txObj = self.nodes[0].gettransaction(txId)
        assert_equal(txObj['amount'], Decimal('-0.0001'))

        # check if JSON parser can handle scientific notation in strings
        txId = self.nodes[0].sendtoaddress(
            self.nodes[2].getnewaddress(), "1e-4")
        txObj = self.nodes[0].gettransaction(txId)
        assert_equal(txObj['amount'], Decimal('-0.0001'))

        # This will raise an exception because the amount type is wrong
        assert_raises_rpc_error(-3, "Invalid amount",
                                self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")

        # This will raise an exception since generate does not accept a string
        assert_raises_rpc_error(-1, "not an integer",
                                self.nodes[0].generate, "2")

        # Import address and private key to check correct behavior of spendable unspents
        # 1. Send some coins to generate new UTXO
        address_to_import = self.nodes[2].getnewaddress()
        txid = self.nodes[0].sendtoaddress(address_to_import, 1)
        self.nodes[0].generate(1)
        self.sync_all([self.nodes[0:3]])

        # 2. Import address from node2 to node1
        self.nodes[1].importaddress(address_to_import)

        # 3. Validate that the imported address is watch-only on node1
        assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"]

        # 4. Check that the unspents after import are not spendable
        assert_array_result(self.nodes[1].listunspent(),
                            {"address": address_to_import},
                            {"spendable": False})

        # 5. Import private key of the previously imported address on node1
        priv_key = self.nodes[2].dumpprivkey(address_to_import)
        self.nodes[1].importprivkey(priv_key)

        # 6. Check that the unspents are now spendable on node1
        assert_array_result(self.nodes[1].listunspent(),
                            {"address": address_to_import},
                            {"spendable": True})

        # Mine a block from node0 to an address from node1
        cbAddr = self.nodes[1].getnewaddress()
        blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0]
        cbTxId = self.nodes[0].getblock(blkHash)['tx'][0]
        self.sync_all([self.nodes[0:3]])

        # Check that the txid and balance is found by node1
        self.nodes[1].gettransaction(cbTxId)

        # check if wallet or blockchain maintenance changes the balance
        self.sync_all([self.nodes[0:3]])
        blocks = self.nodes[0].generate(2)
        self.sync_all([self.nodes[0:3]])
        balance_nodes = [self.nodes[i].getbalance() for i in range(3)]
        block_count = self.nodes[0].getblockcount()

        # Check modes:
        #   - True: unicode escaped as \u....
        #   - False: unicode directly as UTF-8
        for mode in [True, False]:
            self.nodes[0].rpc.ensure_ascii = mode
            # unicode check: Basic Multilingual Plane, Supplementary Plane
            # respectively
            for label in [u'б€б‹аБаА', u'№…Ё']:
                addr = self.nodes[0].getnewaddress()
                self.nodes[0].setlabel(addr, label)
                assert_equal(self.nodes[0].getaddressinfo(
                    addr)['label'], label)
                assert(label in self.nodes[0].listlabels())
        # restore to default
        self.nodes[0].rpc.ensure_ascii = True

        # maintenance tests
        maintenance = [
            '-rescan',
            '-reindex',
            '-zapwallettxes=1',
            '-zapwallettxes=2',
            # disabled until issue is fixed: https://github.com/bitcoin/bitcoin/issues/7463
            # '-salvagewallet',
        ]
        chainlimit = 6
        for m in maintenance:
            self.log.info("check " + m)
            self.stop_nodes()
            # set lower ancestor limit for later
            self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)])
            self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)])
            self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)])
            if m == '-reindex':
                # reindex will leave rpc warm up "early"; Wait for it to finish
                wait_until(lambda: [block_count] * 3 ==
                           [self.nodes[i].getblockcount() for i in range(3)])
            assert_equal(balance_nodes, [
                         self.nodes[i].getbalance() for i in range(3)])

        # Exercise listsinceblock with the last two blocks
        coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])
        assert_equal(coinbase_tx_1["lastblock"], blocks[1])
        assert_equal(len(coinbase_tx_1["transactions"]), 1)
        assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])
        assert_equal(len(self.nodes[0].listsinceblock(
            blocks[1])["transactions"]), 0)

        # ==Check that wallet prefers to use coins that don't exceed mempool li

        # Get all non-zero utxos together
        chain_addrs = [self.nodes[0].getnewaddress(
        ), self.nodes[0].getnewaddress()]
        singletxid = self.nodes[0].sendtoaddress(
            chain_addrs[0], self.nodes[0].getbalance(), "", "", True)
        self.nodes[0].generate(1)
        node0_balance = self.nodes[0].getbalance()
        # Split into two chains
        rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {
                                                   chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')})
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"])
        self.nodes[0].generate(1)

        # Make a long chain of unconfirmed payments without hitting mempool limit
        # Each tx we make leaves only one output of change on a chain 1 longer
        # Since the amount to send is always much less than the outputs, we only ever need one output
        # So we should be able to generate exactly chainlimit txs for each
        # original output
        sending_addr = self.nodes[1].getnewaddress()
        txid_list = []
        for i in range(chainlimit * 2):
            txid_list.append(self.nodes[0].sendtoaddress(
                sending_addr, Decimal('0.0001')))
        assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2)
        assert_equal(len(txid_list), chainlimit * 2)

        # Without walletrejectlongchains, we will still generate a txid
        # The tx will be stored in the wallet but not accepted to the mempool
        extra_txid = self.nodes[0].sendtoaddress(
            sending_addr, Decimal('0.0001'))
        assert extra_txid not in self.nodes[0].getrawmempool()
        assert extra_txid in [tx["txid"]
                              for tx in self.nodes[0].listtransactions()]
        self.nodes[0].abandontransaction(extra_txid)
        total_txs = len(self.nodes[0].listtransactions("*", 99999))

        # Try with walletrejectlongchains
        # Double chain limit but require combining inputs, so we pass
        # SelectCoinsMinConf
        self.stop_node(0)
        self.start_node(0, extra_args=[
                        "-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)])

        # wait for loadmempool
        timeout = 10
        while (timeout > 0 and len(
                self.nodes[0].getrawmempool()) < chainlimit * 2):
            time.sleep(0.5)
            timeout -= 0.5
        assert_equal(len(self.nodes[0].getrawmempool()), chainlimit * 2)

        node0_balance = self.nodes[0].getbalance()
        # With walletrejectlongchains we will not create the tx and store it in
        # our wallet.
        assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain",
                                self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))

        # Verify nothing new in wallet
        assert_equal(total_txs, len(
            self.nodes[0].listtransactions("*", 99999)))

        # Test getaddressinfo on external address. Note that these addresses
        # are taken from disablewallet.py
        assert_raises_rpc_error(-5, "Invalid address",
                                self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy")
        address_info = self.nodes[0].getaddressinfo(
            "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ")
        assert_equal(address_info['address'],
                     "bchreg:qp8rs4qyd3aazk22eyzwg7fmdfzmxm02pywavdajx4")
        assert_equal(address_info["scriptPubKey"],
                     "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac")
        assert not address_info["ismine"]
        assert not address_info["iswatchonly"]
        assert not address_info["isscript"]
        assert not address_info["ischange"]

        # Test getaddressinfo 'ischange' field on change address.
        self.nodes[0].generate(1)
        destination = self.nodes[1].getnewaddress()
        txid = self.nodes[0].sendtoaddress(destination, 0.123)
        tx = self.nodes[0].decoderawtransaction(
            self.nodes[0].getrawtransaction(txid))
        output_addresses = [vout['scriptPubKey']['addresses'][0]
                            for vout in tx["vout"]]
        assert len(output_addresses) > 1
        for address in output_addresses:
            ischange = self.nodes[0].getaddressinfo(address)['ischange']
            assert_equal(ischange, address != destination)
            if ischange:
                change = address
        self.nodes[0].setlabel(change, 'foobar')
        assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)
Exemple #8
0
    def run_test(self):
        print("Mining blocks...")

        min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
        # This test is not meant to test fee estimation and we'd like
        # to be sure all txs are sent at a consistent desired feerate
        for node in self.nodes:
            node.settxfee(min_relay_tx_fee)

        # if the fee's positive delta is higher than this value tests will fail,
        # neg. delta always fail the tests.
        # The size of the signature of every input may be at most 2 bytes larger
        # than a minimum sized signature.

        #            = 2 bytes * minRelayTxFeePerByte
        feeTolerance = 2 * min_relay_tx_fee / 1000

        self.nodes[2].generate(1)
        self.sync_all()
        self.nodes[0].generate(121)
        self.sync_all()

        watchonly_address = self.nodes[0].getnewaddress()
        watchonly_pubkey = self.nodes[0].validateaddress(
            watchonly_address)["pubkey"]
        watchonly_amount = Decimal(200)
        self.nodes[3].importpubkey(watchonly_pubkey, "", True)
        watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address,
                                                     watchonly_amount)
        self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(),
                                    watchonly_amount / 10)

        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.nodes[0].generate(1)
        self.sync_all()

        ###############
        # simple test #
        ###############
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert (len(dec_tx['vin']) > 0)  #test if we have enought inputs

        ##############################
        # simple test with two coins #
        ##############################
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 2.2}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert (len(dec_tx['vin']) > 0)  #test if we have enough inputs

        ##############################
        # simple test with two coins #
        ##############################
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 2.6}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        assert (len(dec_tx['vin']) > 0)
        assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')

        ################################
        # simple test with two outputs #
        ################################
        inputs = []
        outputs = {
            self.nodes[0].getnewaddress(): 2.6,
            self.nodes[1].getnewaddress(): 2.5
        }
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert (len(dec_tx['vin']) > 0)
        assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')

        #########################################################################
        # test a fundrawtransaction with a VIN greater than the required amount #
        #########################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert_equal(fee + totalOut,
                     utx['amount'])  #compare vin total and totalout+fee

        #####################################################################
        # test a fundrawtransaction with which will not get a change output #
        #####################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {
            self.nodes[0].getnewaddress(): Decimal(5.0) - fee - feeTolerance
        }
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        for out in dec_tx['vout']:
            totalOut += out['value']

        assert_equal(rawtxfund['changepos'], -1)
        assert_equal(fee + totalOut,
                     utx['amount'])  #compare vin total and totalout+fee

        ####################################################
        # test a fundrawtransaction with an invalid option #
        ####################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        try:
            self.nodes[2].fundrawtransaction(rawtx, {'foo': 'bar'})
            raise AssertionError("Accepted invalid option foo")
        except JSONRPCException as e:
            assert ("Unexpected key foo" in e.error['message'])

        ############################################################
        # test a fundrawtransaction with an invalid change address #
        ############################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        try:
            self.nodes[2].fundrawtransaction(rawtx,
                                             {'changeAddress': 'foobar'})
            raise AssertionError("Accepted invalid pivxl address")
        except JSONRPCException as e:
            assert ("changeAddress must be a valid pivxl address"
                    in e.error['message'])

        ############################################################
        # test a fundrawtransaction with a provided change address #
        ############################################################
        utx = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): Decimal(4.0)}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        change = self.nodes[2].getnewaddress()
        try:
            rawtxfund = self.nodes[2].fundrawtransaction(
                rawtx, {
                    'changeAddress': change,
                    'changePosition': 2
                })
        except JSONRPCException as e:
            assert ('changePosition out of bounds' == e.error['message'])
        else:
            assert (False)
        rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {
            'changeAddress': change,
            'changePosition': 0
        })
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        out = dec_tx['vout'][0]
        assert_equal(change, out['scriptPubKey']['addresses'][0])

        #########################################################################
        # test a fundrawtransaction with a VIN smaller than the required amount #
        #########################################################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)

        inputs = [{'txid': utx['txid'], 'vout': utx['vout']}]
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)

        # 4-byte version + 1-byte vin count + 36-byte prevout then script_len
        rawtx = rawtx[:82] + "0100" + rawtx[84:]

        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
        assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for i, out in enumerate(dec_tx['vout']):
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts += 1
            else:
                assert_equal(i, rawtxfund['changepos'])

        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
        assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex'])

        assert_equal(matchingOuts, 1)
        assert_equal(len(dec_tx['vout']), 2)

        ###########################################
        # test a fundrawtransaction with two VINs #
        ###########################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)
        utx2 = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{
            'txid': utx['txid'],
            'vout': utx['vout']
        }, {
            'txid': utx2['txid'],
            'vout': utx2['vout']
        }]
        outputs = {self.nodes[0].getnewaddress(): 6.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for out in dec_tx['vout']:
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts += 1

        assert_equal(matchingOuts, 1)
        assert_equal(len(dec_tx['vout']), 2)

        matchingIns = 0
        for vinOut in dec_tx['vin']:
            for vinIn in inputs:
                if vinIn['txid'] == vinOut['txid']:
                    matchingIns += 1

        assert_equal(
            matchingIns,
            2)  #we now must see two vins identical to vins given as params

        #########################################################
        # test a fundrawtransaction with two VINs and two vOUTs #
        #########################################################
        utx = get_unspent(self.nodes[2].listunspent(), 1)
        utx2 = get_unspent(self.nodes[2].listunspent(), 5)

        inputs = [{
            'txid': utx['txid'],
            'vout': utx['vout']
        }, {
            'txid': utx2['txid'],
            'vout': utx2['vout']
        }]
        outputs = {
            self.nodes[0].getnewaddress(): 6.0,
            self.nodes[0].getnewaddress(): 1.0
        }
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)
        assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        fee = rawtxfund['fee']
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
        totalOut = 0
        matchingOuts = 0
        for out in dec_tx['vout']:
            totalOut += out['value']
            if out['scriptPubKey']['addresses'][0] in outputs:
                matchingOuts += 1

        assert_equal(matchingOuts, 2)
        assert_equal(len(dec_tx['vout']), 3)

        ##############################################
        # test a fundrawtransaction with invalid vin #
        ##############################################
        listunspent = self.nodes[2].listunspent()
        inputs = [{
            'txid':
            "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1",
            'vout': 0
        }]  #invalid vin!
        outputs = {self.nodes[0].getnewaddress(): 1.0}
        rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        try:
            rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
            raise AssertionError("Spent more than available")
        except JSONRPCException as e:
            assert ("Insufficient" in e.error['message'])

        ############################################################
        #compare fee of a standard pubkeyhash transaction
        inputs = []
        outputs = {self.nodes[1].getnewaddress(): 1.1}
        rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawTx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a standard pubkeyhash transaction with multiple outputs
        inputs = []
        outputs = {
            self.nodes[1].getnewaddress(): 1.1,
            self.nodes[1].getnewaddress(): 1.2,
            self.nodes[1].getnewaddress(): 0.1,
            self.nodes[1].getnewaddress(): 1.3,
            self.nodes[1].getnewaddress(): 0.2,
            self.nodes[1].getnewaddress(): 0.3
        }
        rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawTx)
        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendmany("", outputs)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a 2of2 multisig p2sh transaction

        # create 2of2 addr
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[1].getnewaddress()

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

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

        inputs = []
        outputs = {mSigObj: 1.1}
        rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawTx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        #compare fee of a standard pubkeyhash transaction

        # create 4of5 addr
        addr1 = self.nodes[1].getnewaddress()
        addr2 = self.nodes[1].getnewaddress()
        addr3 = self.nodes[1].getnewaddress()
        addr4 = self.nodes[1].getnewaddress()
        addr5 = self.nodes[1].getnewaddress()

        addr1Obj = self.nodes[1].validateaddress(addr1)
        addr2Obj = self.nodes[1].validateaddress(addr2)
        addr3Obj = self.nodes[1].validateaddress(addr3)
        addr4Obj = self.nodes[1].validateaddress(addr4)
        addr5Obj = self.nodes[1].validateaddress(addr5)

        mSigObj = self.nodes[1].addmultisigaddress(4, [
            addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'],
            addr4Obj['pubkey'], addr5Obj['pubkey']
        ])

        inputs = []
        outputs = {mSigObj: 1.1}
        rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[0].fundrawtransaction(rawTx)

        #create same transaction over sendtoaddress
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
        signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance)
        ############################################################

        ############################################################
        # spend a 2of2 multisig transaction over fundraw

        # create 2of2 addr
        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']])

        # send 1.2 BTC to msig addr
        txId = self.nodes[0].sendtoaddress(mSigObj, 1.2)
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        oldBalance = self.nodes[1].getbalance()
        inputs = []
        outputs = {self.nodes[1].getnewaddress(): 1.1}
        rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[2].fundrawtransaction(rawTx)

        signedTx = self.nodes[2].signrawtransaction(fundedTx['hex'])
        txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
        self.sync_all()
        self.nodes[1].generate(1)
        self.sync_all()

        # make sure funds are received at node1
        assert_equal(oldBalance + Decimal('1.10000000'),
                     self.nodes[1].getbalance())

        ############################################################
        # locked wallet test
        self.nodes[1].encryptwallet("test")
        self.nodes.pop(1)
        self.stop_nodes()

        self.nodes = self.start_nodes()
        # This test is not meant to test fee estimation and we'd like
        # to be sure all txs are sent at a consistent desired feerate
        for node in self.nodes:
            node.settxfee(min_relay_tx_fee)

        connect_nodes(self.nodes[0], 1)
        connect_nodes(self.nodes[1], 2)
        connect_nodes(self.nodes[0], 2)
        connect_nodes(self.nodes[0], 3)
        self.is_network_split = False
        self.sync_all()

        # drain the keypool
        self.nodes[1].getnewaddress()
        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 1.1}
        rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
        # fund a transaction that requires a new key for the change output
        # creating the key must be impossible because the wallet is locked
        try:
            fundedTx = self.nodes[1].fundrawtransaction(rawTx)
            raise AssertionError("Wallet unlocked without passphrase")
        except JSONRPCException as e:
            assert ('Keypool ran out' in e.error['message'])

        #refill the keypool
        self.nodes[1].walletpassphrase("test", 100)
        self.nodes[1].walletlock()

        try:
            self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.2)
            raise AssertionError("Wallet unlocked without passphrase")
        except JSONRPCException as e:
            assert ('walletpassphrase' in e.error['message'])

        oldBalance = self.nodes[0].getbalance()

        inputs = []
        outputs = {self.nodes[0].getnewaddress(): 1.1}
        rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawTx)

        #now we need to unlock
        self.nodes[1].walletpassphrase("test", 100)
        signedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
        txId = self.nodes[1].sendrawtransaction(signedTx['hex'])
        self.nodes[1].generate(1)
        self.sync_all()

        # make sure funds are received at node1
        assert_equal(oldBalance + Decimal('51.10000000'),
                     self.nodes[0].getbalance())

        ###############################################
        # multiple (~19) inputs tx test | Compare fee #
        ###############################################

        #empty node1, send some small coins from node0 to node1
        self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(),
                                    self.nodes[1].getbalance(), "", "", True)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        for i in range(0, 20):
            self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
        self.nodes[0].generate(1)
        self.sync_all()

        #fund a tx with ~20 small inputs
        inputs = []
        outputs = {
            self.nodes[0].getnewaddress(): 0.15,
            self.nodes[0].getnewaddress(): 0.04
        }
        rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawTx)

        #create same transaction over sendtoaddress
        txId = self.nodes[1].sendmany("", outputs)
        signedFee = self.nodes[1].getrawmempool(True)[txId]['fee']

        #compare fee
        feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
        assert (feeDelta >= 0 and feeDelta <= feeTolerance * 19)  #~19 inputs

        #############################################
        # multiple (~19) inputs tx test | sign/send #
        #############################################

        #again, empty node1, send some small coins from node0 to node1
        self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(),
                                    self.nodes[1].getbalance(), "", "", True)
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()

        for i in range(0, 20):
            self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.01)
        self.nodes[0].generate(1)
        self.sync_all()

        #fund a tx with ~20 small inputs
        oldBalance = self.nodes[0].getbalance()

        inputs = []
        outputs = {
            self.nodes[0].getnewaddress(): 0.15,
            self.nodes[0].getnewaddress(): 0.04
        }
        rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
        fundedTx = self.nodes[1].fundrawtransaction(rawTx)
        fundedAndSignedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
        txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
        self.sync_all()
        self.nodes[0].generate(1)
        self.sync_all()
        assert_equal(oldBalance + Decimal('50.19000000'),
                     self.nodes[0].getbalance())  #0.19+block reward

        #####################################################
        # test fundrawtransaction with OP_RETURN and no vin #
        #####################################################

        rawtx = "0100000000010000000000000000066a047465737400000000"
        dec_tx = self.nodes[2].decoderawtransaction(rawtx)

        assert_equal(len(dec_tx['vin']), 0)
        assert_equal(len(dec_tx['vout']), 1)

        rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
        dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])

        assert_greater_than(len(dec_tx['vin']), 0)  # at least one vin
        assert_equal(len(dec_tx['vout']), 2)  # one change output added

        ##################################################
        # test a fundrawtransaction using only watchonly #
        ##################################################

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): watchonly_amount / 2}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        result = self.nodes[3].fundrawtransaction(rawtx,
                                                  {'includeWatching': True})
        res_dec = self.nodes[0].decoderawtransaction(result["hex"])
        assert_equal(len(res_dec["vin"]), 1)
        assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)

        assert ("fee" in result.keys())
        assert_greater_than(result["changepos"], -1)

        ###############################################################
        # test fundrawtransaction using the entirety of watched funds #
        ###############################################################

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): watchonly_amount}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

        # Backward compatibility test (2nd param is includeWatching)
        result = self.nodes[3].fundrawtransaction(rawtx, True)
        res_dec = self.nodes[0].decoderawtransaction(result["hex"])
        assert_equal(len(res_dec["vin"]), 2)
        assert (res_dec["vin"][0]["txid"] == watchonly_txid
                or res_dec["vin"][1]["txid"] == watchonly_txid)

        assert_greater_than(result["fee"], 0)
        assert_greater_than(result["changepos"], -1)
        assert_equal(
            result["fee"] + res_dec["vout"][result["changepos"]]["value"],
            watchonly_amount / 10)

        signedtx = self.nodes[3].signrawtransaction(result["hex"])
        assert (not signedtx["complete"])
        signedtx = self.nodes[0].signrawtransaction(signedtx["hex"])
        assert (signedtx["complete"])
        self.nodes[0].sendrawtransaction(signedtx["hex"])
        self.nodes[0].generate(1)
        self.sync_all()

        #######################
        # Test feeRate option #
        #######################

        # Make sure there is exactly one input so coin selection can't skew the result
        assert_equal(len(self.nodes[3].listunspent(1)), 1)

        inputs = []
        outputs = {self.nodes[2].getnewaddress(): 1}
        rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
        result = self.nodes[3].fundrawtransaction(
            rawtx)  # uses min_relay_tx_fee (set by settxfee)
        result2 = self.nodes[3].fundrawtransaction(
            rawtx, {"feeRate": 2 * min_relay_tx_fee})
        result3 = self.nodes[3].fundrawtransaction(
            rawtx, {"feeRate": 10 * min_relay_tx_fee})
        result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
        assert_fee_amount(result2['fee'], count_bytes(result2['hex']),
                          2 * result_fee_rate)
        assert_fee_amount(result3['fee'], count_bytes(result3['hex']),
                          10 * result_fee_rate)
Exemple #9
0
    def run_test(self):
        # Check that there's no UTXO on none of the nodes
        assert_equal(len(self.nodes[0].listunspent()), 0)
        assert_equal(len(self.nodes[1].listunspent()), 0)
        assert_equal(len(self.nodes[2].listunspent()), 0)

        self.log.info("Mining blocks...")

        self.nodes[0].generate(1)

        wallet_info = self.nodes[0].getwalletinfo()
        assert_equal(wallet_info['immature_balance'], 5000)
        assert_equal(wallet_info['balance'], 0)

        self.sync_all([self.nodes[0:3]])
        self.nodes[1].generate(101)
        self.sync_all([self.nodes[0:3]])

        assert_equal(self.nodes[0].getbalance(), 5000)
        assert_equal(self.nodes[1].getbalance(), 5000)
        assert_equal(self.nodes[2].getbalance(), 0)

        # Check that only first and second nodes have UTXOs
        utxos = self.nodes[0].listunspent()
        assert_equal(len(utxos), 1)
        assert_equal(len(self.nodes[1].listunspent()), 1)
        assert_equal(len(self.nodes[2].listunspent()), 0)

        self.log.info("test gettxout")
        confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"]
        # First, outputs that are unspent both in the chain and in the
        # mempool should appear with or without include_mempool
        txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=False)
        assert_equal(txout['value'], 5000)
        txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=True)
        assert_equal(txout['value'], 5000)
        
        # Send 21 RVN from 0 to 2 using sendtoaddress call.
        # Locked memory should use at least 32 bytes to sign each transaction
        self.log.info("test getmemoryinfo")
        memory_before = self.nodes[0].getmemoryinfo()
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
        mempool_txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
        memory_after = self.nodes[0].getmemoryinfo()
        assert(memory_before['locked']['used'] + 64 <= memory_after['locked']['used'])

        self.log.info("test gettxout (second part)")
        # utxo spent in mempool should be visible if you exclude mempool
        # but invisible if you include mempool
        txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False)
        assert_equal(txout['value'], 5000)
        txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True)
        assert txout is None
        # new utxo from mempool should be invisible if you exclude mempool
        # but visible if you include mempool
        txout = self.nodes[0].gettxout(mempool_txid, 0, False)
        assert txout is None
        txout1 = self.nodes[0].gettxout(mempool_txid, 0, True)
        txout2 = self.nodes[0].gettxout(mempool_txid, 1, True)
        # note the mempool tx will have randomly assigned indices
        # but 10 will go to node2 and the rest will go to node0
        balance = self.nodes[0].getbalance()
        assert_equal({txout1['value'], txout2['value']}, {10, balance})
        wallet_info = self.nodes[0].getwalletinfo()
        assert_equal(wallet_info['immature_balance'], 0)

        # Have node0 mine a block, thus it will collect its own fee.
        self.nodes[0].generate(1)
        self.sync_all([self.nodes[0:3]])

        # Exercise locking of unspent outputs
        unspent_0 = self.nodes[2].listunspent()[0]
        unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
        self.nodes[2].lockunspent(False, [unspent_0])
        assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
        assert_equal([unspent_0], self.nodes[2].listlockunspent())
        self.nodes[2].lockunspent(True, [unspent_0])
        assert_equal(len(self.nodes[2].listlockunspent()), 0)

        # Have node1 generate 100 blocks (so node0 can recover the fee)
        self.nodes[1].generate(100)
        self.sync_all([self.nodes[0:3]])

        # node0 should end up with 100 btc in block rewards plus fees, but
        # minus the 21 plus fees sent to node2
        assert_equal(self.nodes[0].getbalance(), 10000-21)
        assert_equal(self.nodes[2].getbalance(), 21)

        # Node0 should have two unspent outputs.
        # Create a couple of transactions to send them to node2, submit them through
        # node1, and make sure both node0 and node2 pick them up properly:
        node0utxos = self.nodes[0].listunspent(1)
        assert_equal(len(node0utxos), 2)

        # create both transactions
        txns_to_send = []
        for utxo in node0utxos:
            inputs = []
            outputs = {}
            inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]})
            outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] - 3
            raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
            txns_to_send.append(self.nodes[0].signrawtransaction(raw_tx))

        # Have node 1 (miner) send the transactions
        self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True)
        self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], True)

        # Have node1 mine a block to confirm transactions:
        self.nodes[1].generate(1)
        self.sync_all([self.nodes[0:3]])

        assert_equal(self.nodes[0].getbalance(), 0)
        assert_equal(self.nodes[2].getbalance(), 9994)
        assert_equal(self.nodes[2].getbalance("from1"), 9994-21)

        # Send 10 RVN normal
        address = self.nodes[0].getnewaddress("test")
        fee_per_byte = Decimal('0.001') / 1000
        self.nodes[2].settxfee(fee_per_byte * 1000)
        txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('9984'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
        assert_equal(self.nodes[0].getbalance(), Decimal('10'))

        # Send 10 RVN with subtract fee from amount
        txid = self.nodes[2].sendtoaddress(address, 10, "", "", True)
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal -= Decimal('10')
        assert_equal(self.nodes[2].getbalance(), node_2_bal)
        node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))

        # Sendmany 10 RVN
        txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [])
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_0_bal += Decimal('10')
        node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
        assert_equal(self.nodes[0].getbalance(), node_0_bal)

        # Sendmany 10 RVN with subtract fee from amount
        txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address])
        self.nodes[2].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal -= Decimal('10')
        assert_equal(self.nodes[2].getbalance(), node_2_bal)
        self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))

        # Test ResendWalletTransactions:
        # Create a couple of transactions, then start up a fourth
        # node (nodes[3]) and ask nodes[0] to rebroadcast.
        # EXPECT: nodes[3] should have those transactions in its mempool.
        txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
        txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
        sync_mempools(self.nodes[0:2])

        self.start_node(3)
        connect_nodes_bi(self.nodes, 0, 3)
        sync_blocks(self.nodes)

        relayed = self.nodes[0].resendwallettransactions()
        assert_equal(set(relayed), {txid1, txid2})
        sync_mempools(self.nodes)

        assert(txid1 in self.nodes[3].getrawmempool())

        # Exercise balance rpcs
        assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1)
        assert_equal(self.nodes[0].getunconfirmedbalance(), 1)

        #check if we can list zero value tx as available coins
        #1. create rawtx
        #2. hex-changed one output to 0.0
        #3. sign and send
        #4. check if recipient (node0) can list the zero value tx
        usp = self.nodes[1].listunspent()
        inputs = [{"txid":usp[0]['txid'], "vout":usp[0]['vout']}]
        outputs = {self.nodes[1].getnewaddress(): 4999.998, self.nodes[0].getnewaddress(): 1111.11}

        raw_tx = self.nodes[1].createrawtransaction(inputs, outputs)
        raw_tx = raw_tx.replace("c04fbbde19", "0000000000") #replace 1111.11 with 0.0 (int32)
        self.nodes[1].decoderawtransaction(raw_tx)
        signed_raw_tx = self.nodes[1].signrawtransaction(raw_tx)
        dec_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex'])
        zero_value_txid = dec_raw_tx['txid']
        self.nodes[1].sendrawtransaction(signed_raw_tx['hex'])

        self.sync_all()
        self.nodes[1].generate(1) #mine a block
        self.sync_all()

        unspent_txs = self.nodes[0].listunspent() #zero value tx must be in listunspents output
        found = False
        for uTx in unspent_txs:
            if uTx['txid'] == zero_value_txid:
                found = True
                assert_equal(uTx['amount'], Decimal('0'))
        assert found

        #do some -walletbroadcast tests
        self.stop_nodes()
        self.start_node(0, ["-walletbroadcast=0"])
        self.start_node(1, ["-walletbroadcast=0"])
        self.start_node(2, ["-walletbroadcast=0"])
        connect_nodes_bi(self.nodes,0,1)
        connect_nodes_bi(self.nodes,1,2)
        connect_nodes_bi(self.nodes,0,2)
        self.sync_all([self.nodes[0:3]])

        tx_id_not_broadcasted  = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)
        tx_obj_not_broadcasted = self.nodes[0].gettransaction(tx_id_not_broadcasted)
        self.nodes[1].generate(1) #mine a block, tx should not be in there
        self.sync_all([self.nodes[0:3]])
        assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted

        #now broadcast from another node, mine a block, sync, and check the balance
        self.nodes[1].sendrawtransaction(tx_obj_not_broadcasted['hex'])
        self.nodes[1].generate(1)
        self.sync_all([self.nodes[0:3]])
        node_2_bal += 2
        self.nodes[0].gettransaction(tx_id_not_broadcasted)
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        #create another tx
        self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)

        #restart the nodes with -walletbroadcast=1
        self.stop_nodes()
        self.start_node(0)
        self.start_node(1)
        self.start_node(2)
        connect_nodes_bi(self.nodes,0,1)
        connect_nodes_bi(self.nodes,1,2)
        connect_nodes_bi(self.nodes,0,2)
        sync_blocks(self.nodes[0:3])

        self.nodes[0].generate(1)
        sync_blocks(self.nodes[0:3])
        node_2_bal += 2

        #tx should be added to balance because after restarting the nodes tx should be broadcasted
        assert_equal(self.nodes[2].getbalance(), node_2_bal)

        #send a tx with value in a string (PR#6380 +)
        tx_id  = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
        tx_obj = self.nodes[0].gettransaction(tx_id)
        assert_equal(tx_obj['amount'], Decimal('-2'))

        tx_id  = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001")
        tx_obj = self.nodes[0].gettransaction(tx_id)
        assert_equal(tx_obj['amount'], Decimal('-0.0001'))

        #check if JSON parser can handle scientific notation in strings
        tx_id  = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4")
        tx_obj = self.nodes[0].gettransaction(tx_id)
        assert_equal(tx_obj['amount'], Decimal('-0.0001'))

        # This will raise an exception because the amount type is wrong
        assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")

        # This will raise an exception since generate does not accept a string
        assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2")

        # Import address and private key to check correct behavior of spendable unspents
        # 1. Send some coins to generate new UTXO
        address_to_import = self.nodes[2].getnewaddress()
        self.nodes[0].sendtoaddress(address_to_import, 1)
        self.nodes[0].generate(1)
        self.sync_all([self.nodes[0:3]])

        # 2. Import address from node2 to node1
        self.nodes[1].importaddress(address_to_import)

        # 3. Validate that the imported address is watch-only on node1
        assert(self.nodes[1].validateaddress(address_to_import)["iswatchonly"])

        # 4. Check that the unspents after import are not spendable
        assert_array_result(self.nodes[1].listunspent(),
                           {"address": address_to_import},
                           {"spendable": False})

        # 5. Import private key of the previously imported address on node1
        priv_key = self.nodes[2].dumpprivkey(address_to_import)
        self.nodes[1].importprivkey(priv_key)

        # 6. Check that the unspents are now spendable on node1
        assert_array_result(self.nodes[1].listunspent(),
                           {"address": address_to_import},
                           {"spendable": True})

        # Mine a block from node0 to an address from node1
        cb_address = self.nodes[1].getnewaddress()
        block_hash = self.nodes[0].generatetoaddress(1, cb_address)[0]
        cb_tx_id = self.nodes[0].getblock(block_hash)['tx'][0]
        self.sync_all([self.nodes[0:3]])

        # Check that the txid and balance is found by node1
        self.nodes[1].gettransaction(cb_tx_id)

        # check if wallet or blockchain maintenance changes the balance
        self.sync_all([self.nodes[0:3]])
        blocks = self.nodes[0].generate(2)
        self.sync_all([self.nodes[0:3]])
        balance_nodes = [self.nodes[i].getbalance() for i in range(3)]
        self.nodes[0].getblockcount()

        # Check modes:
        #   - True: unicode escaped as \u....
        #   - False: unicode directly as UTF-8
        for mode in [True, False]:
            self.nodes[0].ensure_ascii = mode
            # unicode check: Basic Multilingual Plane, Supplementary Plane respectively
            for s in [u'б€б‹аБаА', u'№…Ё']:
                addr = self.nodes[0].getaccountaddress(s)
                label = self.nodes[0].getaccount(addr)
                assert_equal(label, s)
                assert(s in self.nodes[0].listaccounts().keys())
        self.nodes[0].ensure_ascii = True # restore to default

        # maintenance tests
        maintenance = "-rescan -reindex -zapwallettxes=1 -zapwallettxes=2"
        chain_limit = 6
        self.log.info("check " + maintenance)
        self.stop_nodes()
        # set lower ancestor limit for later
        self.start_node(0, [maintenance, "-limitancestorcount="+str(chain_limit)])
        self.start_node(1, [maintenance, "-limitancestorcount="+str(chain_limit)])
        self.start_node(2, [maintenance, "-limitancestorcount="+str(chain_limit)])
        assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])

        # Exercise listsinceblock with the last two blocks
        coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])
        assert_equal(coinbase_tx_1["lastblock"], blocks[1])
        assert_equal(len(coinbase_tx_1["transactions"]), 1)
        assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])
        assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0)

        # Check that wallet prefers to use coins that don't exceed mempool limits =====

        # Get all non-zero utxos together
        chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()]
        single_tx_id = self.nodes[0].sendtoaddress(chain_addrs[0], self.nodes[0].getbalance(), "", "", True)
        self.nodes[0].generate(1)
        node0_balance = self.nodes[0].getbalance()
        # Split into two chains
        rawtx = self.nodes[0].createrawtransaction([{"txid":single_tx_id, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')})
        signedtx = self.nodes[0].signrawtransaction(rawtx)
        self.nodes[0].sendrawtransaction(signedtx["hex"])
        self.nodes[0].generate(1)

        # Make a long chain of unconfirmed payments without hitting mempool limit
        # Each tx we make leaves only one output of change on a chain 1 longer
        # Since the amount to send is always much less than the outputs, we only ever need one output
        # So we should be able to generate exactly chainlimit txs for each original output
        sending_addr = self.nodes[1].getnewaddress()
        txid_list = []
        for _ in range(chain_limit*2):
            txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001')))
        assert_equal(self.nodes[0].getmempoolinfo()['size'], chain_limit*2)
        assert_equal(len(txid_list), chain_limit*2)

        # Without walletrejectlongchains, we will still generate a txid
        # The tx will be stored in the wallet but not accepted to the mempool
        assert_raises_rpc_error(-4, "Error: The transaction was rejected! Reason given: too-long-mempool-chain", self.nodes[0].sendtoaddress, sending_addr, Decimal('0.0001'))
        # Get the last transaction and verify it is not in the mempool
        trans_count = len(self.nodes[0].listtransactions("*",99999))
        extra_txid = (self.nodes[0].listtransactions("*",1, trans_count-1))[0]['txid']
        assert(extra_txid not in self.nodes[0].getrawmempool())
        total_txs = len(self.nodes[0].listtransactions("*",99999))

        # Try with walletrejectlongchains
        # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf
        self.stop_node(0)
        self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount="+str(2*chain_limit)])

        # wait for loadmempool
        timeout = 10
        while timeout > 0 and len(self.nodes[0].getrawmempool()) < chain_limit*2:
            time.sleep(0.5)
            timeout -= 0.5
        assert_equal(len(self.nodes[0].getrawmempool()), chain_limit*2)

        node0_balance = self.nodes[0].getbalance()
        # With walletrejectlongchains we will not create the tx and store it in our wallet.
        assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))

        # Verify nothing new in wallet
        assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999)))
Exemple #10
0
    def run_test(self):
        """Main test logic"""
        # prepare
        node = self.nodes[0]
        node2 = self.nodes[1]
        node2.generate(2) # make some coins
        self.sync_all()

        # no coins
        try:
            contract = generate_contract(self.options.tmpdir)
            result = node.publishcontract(contract)
        except Exception as e:
            assert "GetSenderAddr" in repr(e)

        # make sure not in mempool when the tx failed
        assert_equal([],node.getrawmempool())

        node.generate(2) # make some coins
        self.sync_all()
        # 错误的合约
        contract = generate_contract(self.options.tmpdir,err_type = "syntax_err")
        try:
            result = node.publishcontract(contract)
        except Exception as e:
            assert 'expected near' in repr(e)

        # 超大合约
        contract = generate_contract(self.options.tmpdir,err_type = "bigfile") # should be bigfile
        try:
            result = node.publishcontract(contract)
        except Exception as e:
            assert 'code is too large' in repr(e)

        # 测试sdk发布合约的接口
        # TODO 需要单独测试prepublishcode接口
        # prepublishcodeTest
        contract = generate_contract(self.options.tmpdir)
        with open(contract) as fh:
            content = "".join(fh.readlines())
        hex_content = bytes_to_hex_str(bytes(content,encoding='utf-8'))
        coster = node.getnewaddress()
        for i in range(10):
            node.sendtoaddress(coster, 1000)
        node.generate(2)
        sender = coster
        amount = 1
        changeaddress = node.getnewaddress()
        result = node.prepublishcode(hex_content,coster,sender,amount,changeaddress)
        print(result)

        # syntax_err hex content
        contract = generate_contract(self.options.tmpdir,err_type = "syntax_err")
        with open(contract) as fh:
            content = "".join(fh.readlines())
        hex_content_tmp = bytes_to_hex_str(bytes(content,encoding='utf-8'))
        try:
            result = node.prepublishcode(hex_content_tmp, coster, sender, amount, changeaddress)
        except Exception as e:
            assert 'expected near' in repr(e)

        # empty content
        hex_content_tmp = bytes_to_hex_str(bytes("",encoding='utf-8'))
        try:
            result = node.prepublishcode(hex_content_tmp, coster, sender, amount, changeaddress)
        except Exception as e:
            assert 'code data can not empty' in repr(e)

        # not hex
        hex_content_tmp = "not hex data"
        try:
            result = node.prepublishcode(hex_content_tmp, coster, sender, amount, changeaddress)
        except Exception as e:
            assert 'code data must hex data' in repr(e)

        # coster test
        # Invalid address
        contract = generate_contract(self.options.tmpdir)
        contract_id = node.publishcontract(contract)["contractaddress"]
        node1_newaddress = self.nodes[1].getnewaddress()
        self.nodes[1].sendtoaddress(node1_newaddress, 100)
        self.nodes[1].generate(2)
        sync_blocks(self.nodes)
        for coster_tmp in ["","DFGHJK12316547645",contract_id,node1_newaddress]:
            try:
                result = node.prepublishcode(hex_content, coster_tmp, sender, amount, changeaddress)
            except Exception as e:
                assert 'Invalid MagnaChain public key address' in repr(e) or "Invalid MagnaChain fund address" in repr(e)
                continue
        node.generate(1)

        # sender test
        # Invalid address
        for sender_tmp in ["", "DFGHJK12316547645", contract_id, node1_newaddress]:
            try:
                result = node.prepublishcode(hex_content, coster, sender_tmp, amount, changeaddress)
            except Exception as e:
                assert 'Invalid MagnaChain public key address' in repr(e) or "Invalid MagnaChain sender address" in repr(e)
                continue
        node.generate(1)

        # amount test
        for amount_tmp in [10000000000, "10",-1, 0, Decimal("0.0009").quantize(Decimal("0.0000"))]:
            try:
                result = node.prepublishcode(hex_content, coster, sender, amount_tmp, changeaddress)
            except Exception as e:
                assert 'Invalid amount for send' in repr(e) or "Amount out of range" in repr(e) or "Invalid amount" in repr(e)
                continue
        node.generate(1)

        # changeaddress test
        for changeaddress_tmp in ["", "DFGHJK12316547645", contract_id, node1_newaddress]:
            try:
                result = node.prepublishcode(hex_content, coster, sender, amount_tmp, changeaddress)
            except Exception as e:
                assert 'Invalid MagnaChain public key address' in repr(e) or "Invalid MagnaChain change address" in repr(e)
                continue
        node.generate(1)

        # test fee
        # encrypt wallet test with contract
        node.node_encrypt_wallet('test')
        self.stop_node(1)
        self.start_nodes()
        connect_nodes_bi(self.nodes, 0, 1)
        node.walletpassphrase("test", 1)
        time.sleep(2) # wait for timeout
        assert_raises_rpc_error(-13,'Please enter the wallet passphrase with walletpassphrase first',node.publishcontract,contract)
        node.walletpassphrase("test", 300)
        payfee = node.getinfo()['paytxfee']
        relayfee = node.getinfo()['relayfee']
        txid = node.publishcontract(contract)['txid']
        txfee = node.gettransaction(txid)['fee']
        tx_size = count_bytes(node.getrawtransaction(txid))
        node.settxfee(20)
        txid = node.publishcontract(contract)['txid']
        print(txfee,node.gettransaction(txid)['fee'])
        assert abs(node.gettransaction(txid)['fee']) > abs(txfee) and abs(node.gettransaction(txid)['fee']) == 100
        assert_equal(node.gettransaction(txid)['confirmations'],0)
        self.sync_all()
        node2.generate(1)
        self.sync_all()
        assert_equal(node.gettransaction(txid)['confirmations'], 1)
        node.settxfee(payfee)

        # 正确的合约,并且进行重复测试
        j = 2
        contract = generate_contract(self.options.tmpdir)
        for i in range(200):
            balance = node.getbalance()
            result = node.publishcontract(contract)
            diff = balance - node.getbalance()
            assert diff > 0 and diff < 13 ,"publish fee too much:%s"%(diff)  # 该合约的费用基本是固定的,避免修改数值出现比较大的偏差
            self.log.info("publish cost:%s"%(balance - node.getbalance()))
            if i % j == 0:
                # 每个多少个交易后才打一次包
                self.sync_all()
                node.generate(1)
                j = min(64,j * 2)

        node.generate(1)
Exemple #11
0
    def run_test(self):
        self.log.info("Setup wallets...")
        # w0 is a wallet with coinbase rewards
        w0 = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
        # w1 is a regular wallet
        self.nodes[1].createwallet(wallet_name="w1")
        w1 = self.nodes[1].get_wallet_rpc("w1")
        # w2 contains the private keys for w3
        self.nodes[1].createwallet(wallet_name="w2", blank=True)
        w2 = self.nodes[1].get_wallet_rpc("w2")
        xpriv = "tprv8ZgxMBicQKsPfHCsTwkiM1KT56RXbGGTqvc2hgqzycpwbHqqpcajQeMRZoBD35kW4RtyCemu6j34Ku5DEspmgjKdt2qe4SvRch5Kk8B8A2v"
        xpub = "tpubD6NzVbkrYhZ4YkEfMbRJkQyZe7wTkbTNRECozCtJPtdLRn6cT1QKb8yHjwAPcAr26eHBFYs5iLiFFnCbwPRsncCKUKCfubHDMGKzMVcN1Jg"
        if self.options.descriptors:
            w2.importdescriptors([{
                "desc":
                descsum_create("wpkh(" + xpriv + "/0/0/*)"),
                "timestamp":
                "now",
                "range": [0, 100],
                "active":
                True
            }, {
                "desc":
                descsum_create("wpkh(" + xpriv + "/0/1/*)"),
                "timestamp":
                "now",
                "range": [0, 100],
                "active":
                True,
                "internal":
                True
            }])
        else:
            w2.sethdseed(True)

        # w3 is a watch-only wallet, based on w2
        self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
        w3 = self.nodes[1].get_wallet_rpc("w3")
        if self.options.descriptors:
            # Match the privkeys in w2 for descriptors
            res = w3.importdescriptors([{
                "desc":
                descsum_create("wpkh(" + xpub + "/0/0/*)"),
                "timestamp":
                "now",
                "range": [0, 100],
                "keypool":
                True,
                "active":
                True,
                "watchonly":
                True
            }, {
                "desc":
                descsum_create("wpkh(" + xpub + "/0/1/*)"),
                "timestamp":
                "now",
                "range": [0, 100],
                "keypool":
                True,
                "active":
                True,
                "internal":
                True,
                "watchonly":
                True
            }])
            assert_equal(res, [{"success": True}, {"success": True}])

        for _ in range(3):
            a2_receive = w2.getnewaddress()
            if not self.options.descriptors:
                # Because legacy wallets use exclusively hardened derivation, we can't do a ranged import like we do for descriptors
                a2_change = w2.getrawchangeaddress(
                )  # doesn't actually use change derivation
                res = w3.importmulti([{
                    "desc":
                    w2.getaddressinfo(a2_receive)["desc"],
                    "timestamp":
                    "now",
                    "keypool":
                    True,
                    "watchonly":
                    True
                }, {
                    "desc":
                    w2.getaddressinfo(a2_change)["desc"],
                    "timestamp":
                    "now",
                    "keypool":
                    True,
                    "internal":
                    True,
                    "watchonly":
                    True
                }])
                assert_equal(res, [{"success": True}, {"success": True}])

        w0.sendtoaddress(a2_receive, 10)  # fund w3
        self.generate(self.nodes[0], 1)

        if not self.options.descriptors:
            # w4 has private keys enabled, but only contains watch-only keys (from w2)
            # This is legacy wallet behavior only as descriptor wallets don't allow watchonly and non-watchonly things in the same wallet.
            self.nodes[1].createwallet(wallet_name="w4",
                                       disable_private_keys=False)
            w4 = self.nodes[1].get_wallet_rpc("w4")
            for _ in range(3):
                a2_receive = w2.getnewaddress()
                res = w4.importmulti([{
                    "desc":
                    w2.getaddressinfo(a2_receive)["desc"],
                    "timestamp":
                    "now",
                    "keypool":
                    False,
                    "watchonly":
                    True
                }])
                assert_equal(res, [{"success": True}])

            w0.sendtoaddress(a2_receive, 10)  # fund w4
            self.generate(self.nodes[0], 1)

        self.log.info("Send to address...")
        self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       add_to_wallet=True)

        self.log.info("Don't broadcast...")
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             add_to_wallet=False)
        assert (res["hex"])

        self.log.info("Return PSBT...")
        res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, psbt=True)
        assert (res["psbt"])

        self.log.info(
            "Create transaction that spends to address, but don't broadcast..."
        )
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       add_to_wallet=False)
        # conf_target & estimate_mode can be set as argument or option
        res1 = self.test_send(from_wallet=w0,
                              to_wallet=w1,
                              amount=1,
                              arg_conf_target=1,
                              arg_estimate_mode="economical",
                              add_to_wallet=False)
        res2 = self.test_send(from_wallet=w0,
                              to_wallet=w1,
                              amount=1,
                              conf_target=1,
                              estimate_mode="economical",
                              add_to_wallet=False)
        assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"],
                     self.nodes[1].decodepsbt(res2["psbt"])["fee"])
        # but not at the same time
        for mode in ["unset", "economical", "conservative"]:
            self.test_send(
                from_wallet=w0,
                to_wallet=w1,
                amount=1,
                arg_conf_target=1,
                arg_estimate_mode="economical",
                conf_target=1,
                estimate_mode=mode,
                add_to_wallet=False,
                expect_error=
                (-8,
                 "Pass conf_target and estimate_mode either as arguments or in the options object, but not both"
                 ))

        self.log.info("Create PSBT from watch-only wallet w3, sign with w2...")
        res = self.test_send(from_wallet=w3, to_wallet=w1, amount=1)
        res = w2.walletprocesspsbt(res["psbt"])
        assert res["complete"]

        if not self.options.descriptors:
            # Descriptor wallets do not allow mixed watch-only and non-watch-only things in the same wallet.
            # This is specifically testing that w4 ignores its own private keys and creates a psbt with send
            # which is not something that needs to be tested in descriptor wallets.
            self.log.info(
                "Create PSBT from wallet w4 with watch-only keys, sign with w2..."
            )
            self.test_send(from_wallet=w4,
                           to_wallet=w1,
                           amount=1,
                           expect_error=(-4, "Insufficient funds"))
            res = self.test_send(from_wallet=w4,
                                 to_wallet=w1,
                                 amount=1,
                                 include_watching=True,
                                 add_to_wallet=False)
            res = w2.walletprocesspsbt(res["psbt"])
            assert res["complete"]

        self.log.info("Create OP_RETURN...")
        self.test_send(from_wallet=w0, to_wallet=w1, amount=1)
        self.test_send(
            from_wallet=w0,
            data="Hello World",
            expect_error=(
                -8, "Data must be hexadecimal string (not 'Hello World')"))
        self.test_send(from_wallet=w0, data="23")
        res = self.test_send(from_wallet=w3, data="23")
        res = w2.walletprocesspsbt(res["psbt"])
        assert res["complete"]

        self.log.info("Test setting explicit fee rate")
        res1 = self.test_send(from_wallet=w0,
                              to_wallet=w1,
                              amount=1,
                              arg_fee_rate="1",
                              add_to_wallet=False)
        res2 = self.test_send(from_wallet=w0,
                              to_wallet=w1,
                              amount=1,
                              fee_rate="1",
                              add_to_wallet=False)
        assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"],
                     self.nodes[1].decodepsbt(res2["psbt"])["fee"])

        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             fee_rate=7,
                             add_to_wallet=False)
        fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
        assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00007"))

        # "unset" and None are treated the same for estimate_mode
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             fee_rate=2,
                             estimate_mode="unset",
                             add_to_wallet=False)
        fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
        assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00002"))

        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             arg_fee_rate=4.531,
                             add_to_wallet=False)
        fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
        assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00004531"))

        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             arg_fee_rate=3,
                             add_to_wallet=False)
        fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
        assert_fee_amount(fee, count_bytes(res["hex"]), Decimal("0.00003"))

        # Test that passing fee_rate as both an argument and an option raises.
        self.test_send(
            from_wallet=w0,
            to_wallet=w1,
            amount=1,
            arg_fee_rate=1,
            fee_rate=1,
            add_to_wallet=False,
            expect_error=
            (-8,
             "Pass the fee_rate either as an argument, or in the options object, but not both"
             ))

        assert_raises_rpc_error(-8, "Use fee_rate (sat/vB) instead of feeRate",
                                w0.send, {w1.getnewaddress(): 1}, 6,
                                "conservative", 1, {"feeRate": 0.01})

        assert_raises_rpc_error(-3, "Unexpected key totalFee", w0.send,
                                {w1.getnewaddress(): 1}, 6, "conservative", 1,
                                {"totalFee": 0.01})

        for target, mode in product([-1, 0, 1009],
                                    ["economical", "conservative"]):
            self.test_send(
                from_wallet=w0,
                to_wallet=w1,
                amount=1,
                conf_target=target,
                estimate_mode=mode,
                expect_error=(-8,
                              "Invalid conf_target, must be between 1 and 1008"
                              ))  # max value of 1008 per src/policy/fees.h
        msg = 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"'
        for target, mode in product([-1, 0], ["btc/kb", "sat/b"]):
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           conf_target=target,
                           estimate_mode=mode,
                           expect_error=(-8, msg))
        for mode in ["", "foo", Decimal("3.141592")]:
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           conf_target=0.1,
                           estimate_mode=mode,
                           expect_error=(-8, msg))
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           arg_conf_target=0.1,
                           arg_estimate_mode=mode,
                           expect_error=(-8, msg))
            assert_raises_rpc_error(-8, msg, w0.send, {w1.getnewaddress(): 1},
                                    0.1, mode)

        for mode in ["economical", "conservative"]:
            for k, v in {
                    "string": "true",
                    "bool": True,
                    "object": {
                        "foo": "bar"
                    }
            }.items():
                self.test_send(
                    from_wallet=w0,
                    to_wallet=w1,
                    amount=1,
                    conf_target=v,
                    estimate_mode=mode,
                    expect_error=(
                        -3, f"Expected type number for conf_target, got {k}"))

        # Test setting explicit fee rate just below the minimum of 1 sat/vB.
        self.log.info(
            "Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed"
        )
        msg = "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       fee_rate=0.999,
                       expect_error=(-4, msg))
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       arg_fee_rate=0.999,
                       expect_error=(-4, msg))

        self.log.info("Explicit fee rate raises if invalid fee_rate is passed")
        # Test fee_rate with zero values.
        msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"
        for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           fee_rate=zero_value,
                           expect_error=(-4, msg))
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           arg_fee_rate=zero_value,
                           expect_error=(-4, msg))
        msg = "Invalid amount"
        # Test fee_rate values that don't pass fixed-point parsing checks.
        for invalid_value in [
                "", 0.000000001, 1e-09, 1.111111111, 1111111111111111,
                "31.999999999999999999999"
        ]:
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           fee_rate=invalid_value,
                           expect_error=(-3, msg))
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           arg_fee_rate=invalid_value,
                           expect_error=(-3, msg))
        # Test fee_rate values that cannot be represented in sat/vB.
        for invalid_value in [
                0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001",
                "0.00000001", "0.00099999", "31.99999999"
        ]:
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           fee_rate=invalid_value,
                           expect_error=(-3, msg))
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           arg_fee_rate=invalid_value,
                           expect_error=(-3, msg))
        # Test fee_rate out of range (negative number).
        msg = "Amount out of range"
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       fee_rate=-1,
                       expect_error=(-3, msg))
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       arg_fee_rate=-1,
                       expect_error=(-3, msg))
        # Test type error.
        msg = "Amount is not a number or string"
        for invalid_value in [True, {"foo": "bar"}]:
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           fee_rate=invalid_value,
                           expect_error=(-3, msg))
            self.test_send(from_wallet=w0,
                           to_wallet=w1,
                           amount=1,
                           arg_fee_rate=invalid_value,
                           expect_error=(-3, msg))

        # TODO: Return hex if fee rate is below -maxmempool
        # res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)
        # assert res["hex"]
        # hex = res["hex"]
        # res = self.nodes[0].testmempoolaccept([hex])
        # assert not res[0]["allowed"]
        # assert_equal(res[0]["reject-reason"], "...") # low fee
        # assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.000001"))

        self.log.info(
            "If inputs are specified, do not automatically add more...")
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=51,
                             inputs=[],
                             add_to_wallet=False)
        assert res["complete"]
        utxo1 = w0.listunspent()[0]
        assert_equal(utxo1["amount"], 50)
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=51,
                       inputs=[utxo1],
                       expect_error=(-4, "Insufficient funds"))
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=51,
                       inputs=[utxo1],
                       add_inputs=False,
                       expect_error=(-4, "Insufficient funds"))
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=51,
                             inputs=[utxo1],
                             add_inputs=True,
                             add_to_wallet=False)
        assert res["complete"]

        self.log.info("Manual change address and position...")
        self.test_send(
            from_wallet=w0,
            to_wallet=w1,
            amount=1,
            change_address="not an address",
            expect_error=(-5,
                          "Change address must be a valid bitcoin address"))
        change_address = w0.getnewaddress()
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       add_to_wallet=False,
                       change_address=change_address)
        assert res["complete"]
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             add_to_wallet=False,
                             change_address=change_address,
                             change_position=0)
        assert res["complete"]
        assert_equal(
            self.nodes[0].decodepsbt(
                res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["address"],
            change_address)
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             add_to_wallet=False,
                             change_type="legacy",
                             change_position=0)
        assert res["complete"]
        change_address = self.nodes[0].decodepsbt(
            res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["address"]
        assert change_address[0] == "m" or change_address[0] == "n"

        self.log.info("Set lock time...")
        height = self.nodes[0].getblockchaininfo()["blocks"]
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             locktime=height + 1)
        assert res["complete"]
        assert res["txid"]
        txid = res["txid"]
        # Although the wallet finishes the transaction, it can't be added to the mempool yet:
        hex = self.nodes[0].gettransaction(res["txid"])["hex"]
        res = self.nodes[0].testmempoolaccept([hex])
        assert not res[0]["allowed"]
        assert_equal(res[0]["reject-reason"], "non-final")
        # It shouldn't be confirmed in the next block
        self.generate(self.nodes[0], 1)
        assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 0)
        # The mempool should allow it now:
        res = self.nodes[0].testmempoolaccept([hex])
        assert res[0]["allowed"]
        # Don't wait for wallet to add it to the mempool:
        res = self.nodes[0].sendrawtransaction(hex)
        self.generate(self.nodes[0], 1)
        assert_equal(self.nodes[0].gettransaction(txid)["confirmations"], 1)

        self.log.info("Lock unspents...")
        utxo1 = w0.listunspent()[0]
        assert_greater_than(utxo1["amount"], 1)
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             inputs=[utxo1],
                             add_to_wallet=False,
                             lock_unspents=True)
        assert res["complete"]
        locked_coins = w0.listlockunspent()
        assert_equal(len(locked_coins), 1)
        # Locked coins are automatically unlocked when manually selected
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             inputs=[utxo1],
                             add_to_wallet=False)
        assert res["complete"]

        self.log.info("Replaceable...")
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             add_to_wallet=True,
                             replaceable=True)
        assert res["complete"]
        assert_equal(
            self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"],
            "yes")
        res = self.test_send(from_wallet=w0,
                             to_wallet=w1,
                             amount=1,
                             add_to_wallet=True,
                             replaceable=False)
        assert res["complete"]
        assert_equal(
            self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"],
            "no")

        self.log.info("Subtract fee from output")
        self.test_send(from_wallet=w0,
                       to_wallet=w1,
                       amount=1,
                       subtract_fee_from_outputs=[0])

        self.log.info("Include unsafe inputs")
        self.nodes[1].createwallet(wallet_name="w5")
        w5 = self.nodes[1].get_wallet_rpc("w5")
        self.test_send(from_wallet=w0, to_wallet=w5, amount=2)
        self.test_send(from_wallet=w5,
                       to_wallet=w0,
                       amount=1,
                       expect_error=(-4, "Insufficient funds"))
        res = self.test_send(from_wallet=w5,
                             to_wallet=w0,
                             amount=1,
                             include_unsafe=True)
        assert res["complete"]

        self.log.info("External outputs")
        eckey = ECKey()
        eckey.generate()
        privkey = bytes_to_wif(eckey.get_bytes())

        self.nodes[1].createwallet("extsend")
        ext_wallet = self.nodes[1].get_wallet_rpc("extsend")
        self.nodes[1].createwallet("extfund")
        ext_fund = self.nodes[1].get_wallet_rpc("extfund")

        # Make a weird but signable script. sh(wsh(pkh())) descriptor accomplishes this
        desc = descsum_create("sh(wsh(pkh({})))".format(privkey))
        if self.options.descriptors:
            res = ext_fund.importdescriptors([{
                "desc": desc,
                "timestamp": "now"
            }])
        else:
            res = ext_fund.importmulti([{"desc": desc, "timestamp": "now"}])
        assert res[0]["success"]
        addr = self.nodes[0].deriveaddresses(desc)[0]
        addr_info = ext_fund.getaddressinfo(addr)

        self.nodes[0].sendtoaddress(addr, 10)
        self.nodes[0].sendtoaddress(ext_wallet.getnewaddress(), 10)
        self.generate(self.nodes[0], 6)
        ext_utxo = ext_fund.listunspent(addresses=[addr])[0]

        # An external input without solving data should result in an error
        self.test_send(from_wallet=ext_wallet,
                       to_wallet=self.nodes[0],
                       amount=15,
                       inputs=[ext_utxo],
                       add_inputs=True,
                       psbt=True,
                       include_watching=True,
                       expect_error=(-4, "Insufficient funds"))

        # But funding should work when the solving data is provided
        res = self.test_send(
            from_wallet=ext_wallet,
            to_wallet=self.nodes[0],
            amount=15,
            inputs=[ext_utxo],
            add_inputs=True,
            psbt=True,
            include_watching=True,
            solving_data={
                "pubkeys": [addr_info['pubkey']],
                "scripts": [
                    addr_info["embedded"]["scriptPubKey"],
                    addr_info["embedded"]["embedded"]["scriptPubKey"]
                ]
            })
        signed = ext_wallet.walletprocesspsbt(res["psbt"])
        signed = ext_fund.walletprocesspsbt(res["psbt"])
        assert signed["complete"]
        self.nodes[0].finalizepsbt(signed["psbt"])

        res = self.test_send(from_wallet=ext_wallet,
                             to_wallet=self.nodes[0],
                             amount=15,
                             inputs=[ext_utxo],
                             add_inputs=True,
                             psbt=True,
                             include_watching=True,
                             solving_data={"descriptors": [desc]})
        signed = ext_wallet.walletprocesspsbt(res["psbt"])
        signed = ext_fund.walletprocesspsbt(res["psbt"])
        assert signed["complete"]
        self.nodes[0].finalizepsbt(signed["psbt"])

        dec = self.nodes[0].decodepsbt(signed["psbt"])
        for i, txin in enumerate(dec["tx"]["vin"]):
            if txin["txid"] == ext_utxo["txid"] and txin["vout"] == ext_utxo[
                    "vout"]:
                input_idx = i
                break
        psbt_in = dec["inputs"][input_idx]
        # Calculate the input weight
        # (prevout + sequence + length of scriptSig + scriptsig + 1 byte buffer) * WITNESS_SCALE_FACTOR + num scriptWitness stack items + (length of stack item + stack item) * N stack items + 1 byte buffer
        len_scriptsig = len(psbt_in["final_scriptSig"]["hex"]
                            ) // 2 if "final_scriptSig" in psbt_in else 0
        len_scriptsig += len(ser_compact_size(len_scriptsig)) + 1
        len_scriptwitness = (
            sum([(len(x) // 2) + len(ser_compact_size(len(x) // 2))
                 for x in psbt_in["final_scriptwitness"]]) +
            len(psbt_in["final_scriptwitness"]) +
            1) if "final_scriptwitness" in psbt_in else 0
        input_weight = (
            (40 + len_scriptsig) * WITNESS_SCALE_FACTOR) + len_scriptwitness

        # Input weight error conditions
        assert_raises_rpc_error(
            -8,
            "Input weights should be specified in inputs rather than in options.",
            ext_wallet.send,
            outputs={self.nodes[0].getnewaddress(): 15},
            options={
                "inputs": [ext_utxo],
                "input_weights": [{
                    "txid": ext_utxo["txid"],
                    "vout": ext_utxo["vout"],
                    "weight": 1000
                }]
            })

        # Funding should also work when input weights are provided
        res = self.test_send(from_wallet=ext_wallet,
                             to_wallet=self.nodes[0],
                             amount=15,
                             inputs=[{
                                 "txid": ext_utxo["txid"],
                                 "vout": ext_utxo["vout"],
                                 "weight": input_weight
                             }],
                             add_inputs=True,
                             psbt=True,
                             include_watching=True,
                             fee_rate=10)
        signed = ext_wallet.walletprocesspsbt(res["psbt"])
        signed = ext_fund.walletprocesspsbt(res["psbt"])
        assert signed["complete"]
        tx = self.nodes[0].finalizepsbt(signed["psbt"])
        testres = self.nodes[0].testmempoolaccept([tx["hex"]])[0]
        assert_equal(testres["allowed"], True)
        assert_fee_amount(testres["fees"]["base"], testres["vsize"],
                          Decimal(0.0001))