diff options
Diffstat (limited to 'src/wallet')
| -rw-r--r-- | src/wallet/rpcdump.cpp | 7 | ||||
| -rw-r--r-- | src/wallet/rpcwallet.cpp | 82 | ||||
| -rw-r--r-- | src/wallet/test/wallet_tests.cpp | 60 | ||||
| -rw-r--r-- | src/wallet/wallet.cpp | 26 | ||||
| -rw-r--r-- | src/wallet/walletdb.h | 1 |
5 files changed, 124 insertions, 52 deletions
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 14c2e31d9..d55cc68dc 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -167,6 +167,11 @@ void ImportScript(const CScript& script, const string& strLabel, bool isRedeemSc if (!pwalletMain->HaveCScript(script) && !pwalletMain->AddCScript(script)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); ImportAddress(CBitcoinAddress(CScriptID(script)), strLabel); + } else { + CTxDestination destination; + if (ExtractDestination(script, destination)) { + pwalletMain->SetAddressBook(destination, strLabel, "receive"); + } } } @@ -195,6 +200,8 @@ UniValue importaddress(const UniValue& params, bool fHelp) "4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n" "\nNote: This call can take minutes to complete if rescan is true.\n" "If you have the full public key, you should call importpubkey instead of this.\n" + "\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n" + "as change, and not show up in many RPCs.\n" "\nExamples:\n" "\nImport a script with rescan\n" + HelpExampleCli("importaddress", "\"myscript\"") + diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2d4e95911..8538f880f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1011,6 +1011,85 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) return CBitcoinAddress(innerID).ToString(); } +class Witnessifier : public boost::static_visitor<bool> +{ +public: + CScriptID result; + + bool operator()(const CNoDestination &dest) const { return false; } + + bool operator()(const CKeyID &keyID) { + CPubKey pubkey; + if (pwalletMain && pwalletMain->GetPubKey(keyID, pubkey)) { + CScript basescript; + basescript << ToByteVector(pubkey) << OP_CHECKSIG; + CScript witscript = GetScriptForWitness(basescript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } + + bool operator()(const CScriptID &scriptID) { + CScript subscript; + if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { + int witnessversion; + std::vector<unsigned char> witprog; + if (subscript.IsWitnessProgram(witnessversion, witprog)) { + result = scriptID; + return true; + } + CScript witscript = GetScriptForWitness(subscript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } +}; + +UniValue addwitnessaddress(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 1 || params.size() > 1) + { + string msg = "addwitnessaddress \"address\"\n" + "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" + "It returns the witness script.\n" + + "\nArguments:\n" + "1. \"address\" (string, required) An address known to the wallet\n" + + "\nResult:\n" + "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "}\n" + ; + throw runtime_error(msg); + } + + { + LOCK(cs_main); + if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !GetBoolArg("-walletprematurewitness", false)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Segregated witness not enabled on network"); + } + } + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + Witnessifier w; + CTxDestination dest = address.Get(); + bool ret = boost::apply_visitor(w, dest); + if (!ret) { + throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet"); + } + + return CBitcoinAddress(w.result).ToString(); +} struct tallyitem { @@ -2451,7 +2530,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) // parse hex string from parameter CTransaction origTx; - if (!DecodeHexTx(origTx, params[0].get_str())) + if (!DecodeHexTx(origTx, params[0].get_str(), true)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); if (origTx.vout.size() == 0) @@ -2491,6 +2570,7 @@ static const CRPCCommand commands[] = { "hidden", "resendwallettransactions", &resendwallettransactions, true }, { "wallet", "abandontransaction", &abandontransaction, false }, { "wallet", "addmultisigaddress", &addmultisigaddress, true }, + { "wallet", "addwitnessaddress", &addwitnessaddress, true }, { "wallet", "backupwallet", &backupwallet, true }, { "wallet", "dumpprivkey", &dumpprivkey, true }, { "wallet", "dumpwallet", &dumpwallet, true }, diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 387b22358..c6c505898 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -27,7 +27,7 @@ typedef set<pair<const CWalletTx*,unsigned int> > CoinSet; BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) -static CWallet wallet; +static const CWallet wallet; static vector<COutput> vCoins; static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) @@ -188,11 +188,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // empty the wallet and start again, now with fractions of a cent, to test small change avoidance empty_wallet(); - add_coin(0.1*MIN_CHANGE); - add_coin(0.2*MIN_CHANGE); - add_coin(0.3*MIN_CHANGE); - add_coin(0.4*MIN_CHANGE); - add_coin(0.5*MIN_CHANGE); + add_coin(MIN_CHANGE * 1 / 10); + add_coin(MIN_CHANGE * 2 / 10); + add_coin(MIN_CHANGE * 3 / 10); + add_coin(MIN_CHANGE * 4 / 10); + add_coin(MIN_CHANGE * 5 / 10); // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly @@ -207,8 +207,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // if we add more small coins: - add_coin(0.6*MIN_CHANGE); - add_coin(0.7*MIN_CHANGE); + add_coin(MIN_CHANGE * 6 / 10); + add_coin(MIN_CHANGE * 7 / 10); // and try again to make 1.0 * MIN_CHANGE BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); @@ -229,9 +229,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // sometimes it will fail, and so we use the next biggest coin: empty_wallet(); - add_coin(0.5 * MIN_CHANGE); - add_coin(0.6 * MIN_CHANGE); - add_coin(0.7 * MIN_CHANGE); + add_coin(MIN_CHANGE * 5 / 10); + add_coin(MIN_CHANGE * 6 / 10); + add_coin(MIN_CHANGE * 7 / 10); add_coin(1111 * MIN_CHANGE); BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin @@ -239,9 +239,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0) empty_wallet(); - add_coin(0.4 * MIN_CHANGE); - add_coin(0.6 * MIN_CHANGE); - add_coin(0.8 * MIN_CHANGE); + add_coin(MIN_CHANGE * 4 / 10); + add_coin(MIN_CHANGE * 6 / 10); + add_coin(MIN_CHANGE * 8 / 10); add_coin(1111 * MIN_CHANGE); BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount @@ -249,17 +249,17 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // test avoiding small change empty_wallet(); - add_coin(0.05 * MIN_CHANGE); - add_coin(1 * MIN_CHANGE); - add_coin(100 * MIN_CHANGE); + add_coin(MIN_CHANGE * 5 / 100); + add_coin(MIN_CHANGE * 1); + add_coin(MIN_CHANGE * 100); // trying to make 100.01 from these three coins - BOOST_CHECK( wallet.SelectCoinsMinConf(100.01 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 101.05 * MIN_CHANGE); // we should get all coins + BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change - BOOST_CHECK( wallet.SelectCoinsMinConf(99.9 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -310,7 +310,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // add 75 cents in small change. not enough to make 90 cents, // then try making 90 cents. there are multiple competing "smallest bigger" coins, // one of which should be picked at random - add_coin( 5*CENT); add_coin(10*CENT); add_coin(15*CENT); add_coin(20*CENT); add_coin(25*CENT); + add_coin(5 * CENT); + add_coin(10 * CENT); + add_coin(15 * CENT); + add_coin(20 * CENT); + add_coin(25 * CENT); fails = 0; for (int i = 0; i < RANDOM_REPEATS; i++) @@ -345,20 +349,6 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); - - empty_wallet(); - - // Test trimming - for (int i = 0; i < 100; i++) - add_coin(10 * COIN); - for (int i = 0; i < 100; i++) - add_coin(1000 * COIN); - - BOOST_CHECK(wallet.SelectCoinsMinConf(100001 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet)); - // We need all 100 larger coins and exactly one small coin. - // Superfluous small coins must be trimmed from the set: - BOOST_CHECK_EQUAL(nValueRet, 100010 * COIN); - BOOST_CHECK_EQUAL(setCoinsRet.size(), 101); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 723b2ecef..a0095ebd9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1879,16 +1879,6 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns } } } - - //Reduces the approximate best subset by removing any inputs that are smaller than the surplus of nTotal beyond nTargetValue. - for (unsigned int i = 0; i < vValue.size(); i++) - { - if (vfBest[i] && (nBest - vValue[i].first) >= nTargetValue ) - { - vfBest[i] = false; - nBest -= vValue[i].first; - } - } } bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, @@ -2334,33 +2324,37 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt { bool signSuccess; const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; - CScript& scriptSigRes = txNew.vin[nIn].scriptSig; + SignatureData sigdata; if (sign) - signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes); + signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata); else - signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes); + signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); if (!signSuccess) { strFailReason = _("Signing transaction failed"); return false; + } else { + UpdateTransaction(txNew, nIn, sigdata); } + nIn++; } - unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + unsigned int nBytes = GetVirtualTransactionSize(txNew); // Remove scriptSigs if we used dummy signatures for fee calculation if (!sign) { BOOST_FOREACH (CTxIn& vin, txNew.vin) vin.scriptSig = CScript(); + txNew.wit.SetNull(); } // Embed the constructed transaction data in wtxNew. *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); // Limit size - if (nBytes >= MAX_STANDARD_TX_SIZE) + if (GetTransactionCost(txNew) >= MAX_STANDARD_TX_COST) { strFailReason = _("Transaction too large"); return false; @@ -3209,7 +3203,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after bip32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); + strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 71b0ff26d..d083722dd 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_WALLETDB_H #include "amount.h" +#include "primitives/transaction.h" #include "wallet/db.h" #include "key.h" |