From 018828d66647fc30a6f2ff0be303ea8eca921fbe Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 17 Dec 2014 17:49:18 +0100 Subject: [PATCH] fundrawtransaction basics --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/rawtransactions.py | 52 +++++++++++++++++++++++++++++++ src/qt/walletmodel.cpp | 3 +- src/rpcclient.cpp | 1 + src/rpcrawtransaction.cpp | 54 +++++++++++++++++++++++++++++++++ src/rpcserver.cpp | 5 ++- src/rpcserver.h | 1 + src/rpcwallet.cpp | 3 +- src/wallet.cpp | 31 ++++++++++++++++--- src/wallet.h | 5 +-- 10 files changed, 146 insertions(+), 10 deletions(-) create mode 100755 qa/rpc-tests/rawtransactions.py diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index d6ee00bb7d..b374db0e78 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -26,6 +26,7 @@ if [ "x${ENABLE_BITCOIND}${ENABLE_UTILS}${ENABLE_WALLET}" = "x111" ]; then ${BUILDDIR}/qa/rpc-tests/mempool_spendcoinbase.py --srcdir "${BUILDDIR}/src" ${BUILDDIR}/qa/rpc-tests/httpbasics.py --srcdir "${BUILDDIR}/src" ${BUILDDIR}/qa/rpc-tests/mempool_coinbase_spends.py --srcdir "${BUILDDIR}/src" + ${BUILDDIR}/qa/rpc-tests/rawtransactions.py --srcdir "${BUILDDIR}/src" #${BUILDDIR}/qa/rpc-tests/forknotify.py --srcdir "${BUILDDIR}/src" else echo "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py new file mode 100755 index 0000000000..2e20a5d89e --- /dev/null +++ b/qa/rpc-tests/rawtransactions.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test re-org scenarios with a mempool that contains transactions +# that spend (directly or indirectly) coinbase transactions. +# + +from test_framework import BitcoinTestFramework +from util import * + +# Create one-input, one-output, no-fee transaction: +class RawTransactionsTest(BitcoinTestFramework): + + def run_test(self): + + newAddr = self.nodes[2].getnewaddress() + + inputs = [] + outputs = { newAddr : 10 } + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + rawtxfund = self.nodes[0].fundrawtransaction(rawtx) + dec_rawtxfund = self.nodes[0].decoderawtransaction(rawtxfund['hex']) + + assert_equal(len(dec_rawtxfund['vin']), 1) + assert_equal(len(dec_rawtxfund['vout']), 2) + assert_equal(rawtxfund['fee'] > 0, True) + assert_equal(dec_rawtxfund['vin'][0]['scriptSig']['hex'], '') + + rawtxfundsigned = self.nodes[0].signrawtransaction(rawtxfund['hex']) + dec_rawtxfundsigned = self.nodes[0].decoderawtransaction(rawtxfundsigned['hex']) + + assert_equal(len(dec_rawtxfundsigned['vin'][0]['scriptSig']['hex']) > 20, True) + + + listunspent = self.nodes[2].listunspent() + inputs = [{'txid' : listunspent[0]['txid'], 'vout' : listunspent[0]['vout']}] + outputs = { newAddr : 10 } + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + aException = False + try: + rawtxfund = self.nodes[0].fundrawtransaction(rawtx) + except JSONRPCException,e: + aException = True + + assert_equal(aException, True) + + +if __name__ == '__main__': + RawTransactionsTest().main() diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index b20465794d..75172d6340 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -263,8 +263,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); + CMutableTransaction newMTx; CReserveKey *keyChange = transaction.getPossibleKeyChange(); - bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl); + bool fCreated = wallet->CreateTransaction(vecSend, *newTx, newMTx, *keyChange, nFeeRequired, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); if(!fCreated) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 03ce9acbbf..a4e5826699 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -74,6 +74,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "signrawtransaction", 1 }, { "signrawtransaction", 2 }, { "sendrawtransaction", 1 }, + { "fundrawtransaction", 1 }, { "gettxout", 1 }, { "gettxout", 2 }, { "lockunspent", 0 }, diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index e243dae421..c73bc40c8f 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -756,3 +756,57 @@ Value sendrawtransaction(const Array& params, bool fHelp) return hashTx.GetHex(); } + +#ifdef ENABLE_WALLET +Value fundrawtransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "fundrawtransaction \"hexstring\"\n" + "\nAdd vIns to a raw transaction.\n" + "\nAlso see createrawtransaction and signrawtransaction calls.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "\nResult:\n" + "{\n" + " \"hex\": \"value\", (string) The raw transaction with vIns (hex-encoded string)\n" + " \"fee\": n calculated fee\n" + "}\n" + "\"hex\" \n" + "\nExamples:\n" + "\nCreate a transaction with empty vIns\n" + + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + + "\nFund the transaction, and get back the hex\n" + + HelpExampleCli("fundrawtransaction", "\"myhex\"") + + "\nSign the transaction, and get back the hex\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + "\nSend the transaction (signed hex)\n" + + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + + "\nAs a json rpc call\n" + + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") + ); + + RPCTypeCheck(params, list_of(str_type)(bool_type)); + + // parse hex string from parameter + CTransaction tx; + if (!DecodeHexTx(tx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + if (tx.vin.size() > 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "fundrawtransaction only supports transactions with zero exiting vins"); + + CMutableTransaction txNew; + CAmount nFeeRet; + string strFailReason; + if(!pwalletMain->FundTransaction(tx, txNew, nFeeRet, strFailReason)) + throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + + Object result; + result.push_back(Pair("hex", EncodeHexTx(txNew))); + result.push_back(Pair("fee", nFeeRet)); + + return result; +} + +#endif diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 37e4f48a3d..7d48508bb9 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -293,7 +293,10 @@ static const CRPCCommand vRPCCommands[] = { "rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false }, { "rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */ - +#ifdef ENABLE_WALLET + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, false, true }, +#endif + /* Utility functions */ { "util", "createmultisig", &createmultisig, true, true , false }, { "util", "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ diff --git a/src/rpcserver.h b/src/rpcserver.h index 2b2428445d..a60f7d5e6a 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -207,6 +207,7 @@ extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value decodescript(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value fundrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index a169392216..3adb69f051 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -901,7 +901,8 @@ Value sendmany(const Array& params, bool fHelp) CReserveKey keyChange(pwalletMain); CAmount nFeeRequired = 0; string strFailReason; - bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); + CMutableTransaction newTx; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, newTx, keyChange, nFeeRequired, strFailReason); if (!fCreated) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); if (!pwalletMain->CommitTransaction(wtx, keyChange)) diff --git a/src/wallet.cpp b/src/wallet.cpp index d2df7a25f9..25ce41a117 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1358,10 +1358,23 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set > vecSend; + + BOOST_FOREACH (const CTxOut& out, txToFund.vout) + { + vecSend.push_back(make_pair(out.scriptPubKey, out.nValue)); + } + + CReserveKey reservekey(this); + CWalletTx wtx; + return CreateTransaction(vecSend, wtx, txNew, reservekey, nFeeRet, strFailReason, NULL, false); +} bool CWallet::CreateTransaction(const vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) + CWalletTx& wtxNew, CMutableTransaction& txNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign) { CAmount nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend) @@ -1381,7 +1394,6 @@ bool CWallet::CreateTransaction(const vector >& vecSend, wtxNew.fTimeReceivedIsTxTime = true; wtxNew.BindWallet(this); - CMutableTransaction txNew; { LOCK2(cs_main, cs_wallet); @@ -1502,6 +1514,14 @@ bool CWallet::CreateTransaction(const vector >& vecSend, strFailReason = _("Transaction too large"); return false; } + + //remove signature if we used the signing only for the fee calculation + if(!sign) + { + BOOST_FOREACH (CTxIn& vin, txNew.vin) + vin.scriptSig = CScript(); + } + dPriority = wtxNew.ComputePriority(dPriority, nBytes); // Can we complete this as a free transaction? @@ -1541,11 +1561,12 @@ bool CWallet::CreateTransaction(const vector >& vecSend, } bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) + CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl); + CMutableTransaction txNew; + return CreateTransaction(vecSend, wtxNew, txNew, reservekey, nFeeRet, strFailReason, coinControl, sign); } /** diff --git a/src/wallet.h b/src/wallet.h index f3b11ce4b6..88c541f35c 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -288,10 +288,11 @@ public: CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; + bool FundTransaction(const CTransaction& txToFund, CMutableTransaction& txNew, CAmount& nFeeRet, std::string& strFailReason); bool CreateTransaction(const std::vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + CWalletTx& wtxNew, CMutableTransaction& txNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = false); bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = false); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); static CFeeRate minTxFee;