Skip to content

Commit

Permalink
fundrawtransaction basics
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasschnelli authored and TheBlueMatt committed Jun 5, 2015
1 parent 625eff8 commit 018828d
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 10 deletions.
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
52 changes: 52 additions & 0 deletions qa/rpc-tests/rawtransactions.py
Original file line number Diff line number Diff line change
@@ -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()
3 changes: 2 additions & 1 deletion src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/rpcclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "signrawtransaction", 1 },
{ "signrawtransaction", 2 },
{ "sendrawtransaction", 1 },
{ "fundrawtransaction", 1 },
{ "gettxout", 1 },
{ "gettxout", 2 },
{ "lockunspent", 0 },
Expand Down
54 changes: 54 additions & 0 deletions src/rpcrawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 4 additions & 1 deletion src/rpcserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions src/rpcserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion src/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
31 changes: 26 additions & 5 deletions src/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1358,10 +1358,23 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
}



bool CWallet::FundTransaction(const CTransaction& txToFund, CMutableTransaction& txNew, CAmount &nFeeRet, std::string& strFailReason)
{

vector<pair<CScript, CAmount> > 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<pair<CScript, CAmount> >& 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)
Expand All @@ -1381,7 +1394,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,

wtxNew.fTimeReceivedIsTxTime = true;
wtxNew.BindWallet(this);
CMutableTransaction txNew;

{
LOCK2(cs_main, cs_wallet);
Expand Down Expand Up @@ -1502,6 +1514,14 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& 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?
Expand Down Expand Up @@ -1541,11 +1561,12 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& 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<CScript, CAmount> > 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);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
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<std::pair<CScript, CAmount> >& 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;
Expand Down

0 comments on commit 018828d

Please sign in to comment.