fundrawtransaction
accepts a boolean for the second parameter. With this change the second parameter can be either a boolean or a JSON object with the following optional keys: includeWatching
, changeAddress
and changePosition
.
fundrawtransaction
accepts a boolean for the second parameter. With this change the second parameter can be either a boolean or a JSON object with the following optional keys: includeWatching
, changeAddress
and changePosition
.
2467@@ -2468,7 +2468,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
2468 + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
2469 );
2470
2471- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
2472+ // RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
734@@ -735,7 +735,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
735 * Insert additional inputs into the transaction by
736 * calling CreateTransaction();
737 */
738- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching);
739+ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching, const CTxDestination& destChange);
git add -p
.
2471@@ -2468,7 +2472,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
2472 + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
2473 );
2474
2475- RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
2476+ RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
376@@ -377,7 +377,7 @@ bool CWallet::Verify(const string& walletFile, string& warningString, string& er
377 } catch (const boost::filesystem::filesystem_error&) {
378 // failure is ok (well, not really, but it's not worse than what we started with)
379 }
380-
381+
2481@@ -2478,15 +2482,31 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
2482 if (origTx.vout.size() == 0)
2483 throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
2484
2485+ CBitcoinAddress changeAddress;
600@@ -575,7 +601,7 @@ def run_test(self):
601 outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
602 rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
603
604- result = self.nodes[3].fundrawtransaction(rawtx, True)
605+ result = self.nodes[3].fundrawtransaction(rawtx, {'includeWatching': True })
616@@ -591,6 +617,7 @@ def run_test(self):
617 outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
618 rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
619
620+ # Backward compatibility test (2nd param is includeWatching)
621 result = self.nodes[3].fundrawtransaction(rawtx, True)
734@@ -735,7 +735,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
735 * Insert additional inputs into the transaction by
736 * calling CreateTransaction();
737 */
738- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching);
I feel we should have a better argument order. Suggestion:
0bool FundTransaction(CMutableTransaction& tx, const CTxDestination& changeDestination, int& changePosition, bool includeWatching, CAmount& nFeeRet, std::string& strFailReason);
nChangePosRet
to nChangePosInOut
and add a comment in the header that -1 as a input value will result in setting a random position.
2479+ CTxDestination changeAddress = CNoDestination();
2480+ int changePosition = -1;
2481+ bool includeWatching = false;
2482+
2483+ if (params.size() > 1) {
2484+ if (params[1].type() == UniValue::VBOOL) {
2135@@ -2134,14 +2136,17 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
2136 // add the dust to the fee.
2137 if (newTxOut.IsDust(::minRelayTxFee))
2138 {
2139+ nChangePos = -1;
2144 {
2145- // Insert change txn at random position:
2146- nChangePosRet = GetRandInt(txNew.vout.size()+1);
2147- vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosRet;
2148+ if (nChangePos == -1) {
2149+ // Insert change txn at random position:
2487+ else {
2488+ RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
2489+
2490+ UniValue options = params[1];
2491+
2492+ if (options.exists("changeAddress"))
2486+ }
2487+ else {
2488+ RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
2489+
2490+ UniValue options = params[1];
2491+
RPCTypeCheckObj()
to ensure that changePosition
, etc. are the correct types? Otherwise you get a plain JSON value is not a boolean as expected
(etc.)?
utACK. Nice work!
I’m not sure if we should add the backward compatibility fallback (includeWatchonly = true). Because it could also lead to problems in future. If so, we should also mention in in the RPC help (something like a plain True results in includeWatchonly=true
).
IMO a API break would be acceptable.
sendrawtransaction
?
fundrawtransactions
calls selecting the same unspents.
lockUnspent(false, [unspents])
manually, those unspents will remain unlocked until bitcoind is restarted or lockUnspent(true, [unspents])
is called. I did not see a code path where these unspents are cleaned up - removed from the “locked” list but still unavailable for future spending.
@ruimarinho: Yes. There is no such thing as a auto-release because the intention is the keep these unspents locked.
But if you lock the unspents in fundrawtransaction
by default, you need to unlock them in case you don’t sign or send them (or in cases of an error).
But I agree, there are use-cases where you want to directly lock the unspent in the fundrawtransaction call (concurrency).
lockunspent
and don’t use it.
1940 // Add new txins (keeping original txin scriptSig/order)
1941 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1942 {
1943 if (!coinControl.IsSelected(txin.prevout))
1944 tx.vin.push_back(txin);
1945+ }
nChangePosRet
to nChangePosInOut
. I wonder if other *Ret
should be renamed to *Out
?
1907@@ -1908,7 +1908,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
1908 return res;
1909 }
1910
1911-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching)
1912+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, const CTxDestination& destChange)
bool lockUnspents,
argument.
I think this PR should be in 0.13 (once finalized)! Tested a bit after fixing the compile error (see above).
Some issues:
1) I called
fundrawtransaction 01000000000100e1f505000000001976a914f592d5ff0f4cad760d23601e32714639c244d27588ac00000000 '{"changeAddress":"mfrczJz4p5ziD4CiVTip3R7jjXJAjDKBmf", "includeWatching":true, "changePos":0}'
I used changePos
but the valid argument would be changePosition
. An error in a such case would be nice.
2)
Also, I used a invalid change address…
fundrawtransaction 01000000000100e1f505000000001976a914f592d5ff0f4cad760d23601e32714639c244d27588ac00000000 '{"changeAddress":"dontforget.to.call@your.mother", "includeWatching":true, "changePosition":0}'
… which was accepted (no error or warning) but not used. It used one from the wallets pool (which could be dangerous in a watchOnly-fund-environment.
Happy to test again.
Please review. @jonasschnelli FYI
@jonasschnelli, since you’re more involved in this PR, what do you think of @promag’s suggestion above?
do you think it’s acceptable to specify a fixed fee and bypass the minimal fee calculation through iteration in CWallet::CreateTransaction?
CWallet::CreateTransaction
.
@ruimarinho: Yes. A common problems of Bitcoin Core pulls is the “expanding scope”. Keep it simple, add other features later in a different PR.
I think we need to distinct FRT and keep it – relatively simple. If we add options to set the fee, the only thing thats left is the coin selection then? not? Maybe someone could just grab the unspents over listunspents
and do his own coin selection and create a final transaction with createrawtransaction
.
And if you know the changeoutputs address, you could rewrite the transaction after FRT (on your side of the application) and change its fee. @promag: thanks. Will re-test soon.
And if you know the changeoutputs address, you could rewrite the transaction after FRT (on your side of the application) and change its fee. @jonasschnelli this is not entirely correct because there may be a better subset of unspents to better satisfy the output value + fixed fee. But since the fee is very low this may be a good tradeoff.
Edit:
Also there this special case when no change is created, so the application has to create one to adjust the difference.
2nd edit::
And the worst case is when the total input value of the selected unspents isn’t enough to cover the fixed fee. So I would say that having the option to specify the fixed fee is the way to go.
the only thing thats left is the coin selection then @jonasschnelli actually this is accomplished with
createrawtransaction
.
[…] And the worst case is when the total input value of the selected unspents isn’t enough to cover the fixed fee. So I would say that having the option to specify the fixed fee is the way to go.
Yes. This is a point.
the only thing thats left is the coin selection then
actually this is accomplished with createrawtransaction.
createrawtransaction does not use coinselection IMO. FRT does.
1943 if (!coinControl.IsSelected(txin.prevout))
1944+ {
1945 tx.vin.push_back(txin);
1946+
1947+ if (lockUnspents)
1948+ LockCoin(txin.prevout);
cs_wallet
for this operation.
I think between L1944 and L1945 you should add a LOCK2(cs_main, cs_wallet);
2456+ "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
2457+ "2. options (object, optional)\n"
2458+ " {\n"
2459+ " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
2460+ " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
2461+ " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
includeWatchOnly
to follow a consistent nomenclature?
allowWatchOnly
. includeWatching
is the old parameter name. Happy to rename if others agree.
includeWatching
.
2450@@ -2451,8 +2451,14 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
2451 "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
2452 "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
2453 "\nArguments:\n"
2454- "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
2455- "2. includeWatching (boolean, optional, default false) Also select inputs which are watch only\n"
2456+ "1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
2457+ "2. options (object, optional)\n"
true
instead of an object will result in {"includeWatching":true}"
1933 return false;
1934
1935- if (nChangePosRet != -1)
1936- tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]);
1937+ if (nChangePosInOut != -1)
1938+ tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.vout[nChangePosInOut]);
nChangePosInOut = 10
but tx.vout.size()==2
?
We need a out-of-range exception in a such case.
@promag: The check should should really be here (maybe an additional check). If someone uses CWallet::FundTransaction()
with a nChangePosInOut >= wtx.vout.size()
, the app crashes/throws an exceptions. Future changes might easy get trapped by this.
The problematic part is wtx.vout[nChangePosInOut]
. There should be a check somewhere in CWallet::FundTransaction()
that leads to a return false
if a overflow has detected.
Allmost done. Last iteration:
changePosition
.true
instead of an parameter object)ping @promag: Would be nice if you could take the change output order check into CreateTransaction()
: https://github.com/bitcoin/bitcoin/pull/7518/files#r58496250
I’m convince that this is the last issue before this is ready for merging.
Please also fix the commit order. The head commit is from March 5, but the HEAD~2 is March 30.
0commit 4b0360f4160e059b61a21a95a6106d0eed927bd9
1Author: João Barbosa <joao@uphold.com>
2Date: Sat Mar 5 09:45:57 2016 +0000
3
4commit c2a3ff1a08df8619f7af77ebe6c3b7661e858bc1
5Author: João Barbosa <joao@uphold.com>
6Date: Wed Mar 30 02:04:22 2016 +0100
7
8commit 699720b3baa9e0d3dab679b1da91ab45808aa85e
9Author: João Barbosa <joao@uphold.com>
10Date: Wed Mar 30 00:59:29 2016 +0100
Strict flag forces type check on all object keys.
2181+ if (nChangePosInOut == -1)
2182+ {
2183+ // Insert change txn at random position:
2184+ nChangePosInOut = GetRandInt(txNew.vout.size()+1);
2185+ }
2186+ else if (nChangePosInOut > txNew.vout.size())
Tested ACK 18be394cd8cf5b021e25fff34066f1a3a002858c.
This is very useful. It allows to fully decouple the private keys from the node/wallet.
Merged this as be14ca5, with a trivial rebase (only overlap in test framework) to master