diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/db.cpp | 12 | ||||
-rw-r--r-- | src/wallet/db.h | 4 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 9 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 60 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 137 | ||||
-rw-r--r-- | src/wallet/wallet.h | 17 |
6 files changed, 216 insertions, 23 deletions
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 53cfcf0961..e5bc653c33 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -43,7 +43,7 @@ void CDBEnv::EnvShutdown() if (ret != 0) LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); if (!fMockDb) - DbEnv(0).remove(path.string().c_str(), 0); + DbEnv(0).remove(strPath.c_str(), 0); } void CDBEnv::Reset() @@ -78,10 +78,10 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) boost::this_thread::interruption_point(); - path = pathIn; - boost::filesystem::path pathLogDir = path / "database"; + strPath = pathIn.string(); + boost::filesystem::path pathLogDir = pathIn / "database"; TryCreateDirectory(pathLogDir); - boost::filesystem::path pathErrorFile = path / "db.log"; + boost::filesystem::path pathErrorFile = pathIn / "db.log"; LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; @@ -98,7 +98,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) dbenv->set_flags(DB_AUTO_COMMIT, 1); dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); - int ret = dbenv->open(path.string().c_str(), + int ret = dbenv->open(strPath.c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -455,7 +455,7 @@ void CDBEnv::Flush(bool fShutdown) dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); if (!fMockDb) - boost::filesystem::remove_all(path / "database"); + boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database"); } } } diff --git a/src/wallet/db.h b/src/wallet/db.h index 2df6f6e5a9..64071caa3a 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -27,7 +27,9 @@ class CDBEnv private: bool fDbEnvInit; bool fMockDb; - boost::filesystem::path path; + // Don't change into boost::filesystem::path, as that can result in + // shutdown problems/crashes caused by a static initialized internal pointer. + std::string strPath; void EnvShutdown(); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index b682a42e23..5f800474a0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -94,6 +94,9 @@ UniValue importprivkey(const UniValue& params, bool fHelp) + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") ); + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing keys is disabled in pruned mode"); + LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); @@ -166,6 +169,9 @@ UniValue importaddress(const UniValue& params, bool fHelp) + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false") ); + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing addresses is disabled in pruned mode"); + LOCK2(cs_main, pwalletMain->cs_wallet); CScript script; @@ -236,6 +242,9 @@ UniValue importwallet(const UniValue& params, bool fHelp) + HelpExampleRpc("importwallet", "\"test\"") ); + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); + LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index d284fcf15c..8d88933878 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -733,12 +733,12 @@ UniValue getbalance(const UniValue& params, bool fHelp) if (params[0].get_str() == "*") { // Calculate total balance a different way from GetBalance() // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' 0 should return the same number + // getbalance and "getbalance * 1 true" should return the same number CAmount nBalance = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0) + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; CAmount allFee; @@ -2201,6 +2201,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in btc/kb\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2219,6 +2220,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); if (pwalletMain->IsCrypted()) obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); return obj; } @@ -2359,3 +2361,57 @@ UniValue listunspent(const UniValue& params, bool fHelp) return results; } + +UniValue fundrawtransaction(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 1) + throw runtime_error( + "fundrawtransaction \"hexstring\"\n" + "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" + "This will not modify existing inputs, and will add one change output to the outputs.\n" + "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" + "The inputs added will not be signed, use signrawtransaction for that.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "\nResult:\n" + "{\n" + " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" + " \"fee\": n, (numeric) The fee added to the transaction\n" + " \"changepos\": n (numeric) The position of the added change output, or -1\n" + "}\n" + "\"hex\" \n" + "\nExamples:\n" + "\nCreate a transaction with no inputs\n" + + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + + "\nAdd sufficient unsigned inputs to meet the output value\n" + + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + + "\nSign the transaction\n" + + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + + "\nSend the transaction\n" + + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + // parse hex string from parameter + CTransaction origTx; + if (!DecodeHexTx(origTx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + CMutableTransaction tx(origTx); + CAmount nFee; + string strFailReason; + int nChangePos = -1; + if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason)) + throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(tx))); + result.push_back(Pair("changepos", nChangePos)); + result.push_back(Pair("fee", ValueFromAmount(nFee))); + + return result; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3f12d88e79..44e28de0cd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -419,6 +419,7 @@ void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range) const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; + if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose @@ -1509,7 +1510,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const isminetype mine = IsMine(pcoin->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && - (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i))) vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); } } @@ -1669,25 +1670,108 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx* AvailableCoins(vCoins, true, coinControl); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) - if (coinControl && coinControl->HasSelected()) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) { BOOST_FOREACH(const COutput& out, vCoins) { - if(!out.fSpendable) - continue; + if (!out.fSpendable) + continue; nValueRet += out.tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); } - return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || - (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet))); + // calculate value from preset inputs and store them + set<pair<const CWalletTx*, uint32_t> > setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector<COutPoint> vPresetInputs; + if (coinControl) + coinControl->ListSelected(vPresetInputs); + BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) + { + map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx* pcoin = &it->second; + // Clearly invalid input, fail + if (pcoin->vout.size() <= outpoint.n) + return false; + nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue; + setPresetCoins.insert(make_pair(pcoin, outpoint.n)); + } else + return false; // TODO: Allow non-wallet inputs + } + + // remove preset inputs from vCoins + for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) + { + if (setPresetCoins.count(make_pair(it->tx, it->i))) + it = vCoins.erase(it); + else + ++it; + } + + bool res = nTargetValue <= nValueFromPresetInputs || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet)); + + // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; +} + +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason) +{ + vector<CRecipient> vecSend; + + // Turn the txout set into a CRecipient vector + BOOST_FOREACH(const CTxOut& txOut, tx.vout) + { + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; + vecSend.push_back(recipient); + } + + CCoinControl coinControl; + coinControl.fAllowOtherInputs = true; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + coinControl.Select(txin.prevout); + + CReserveKey reservekey(this); + CWalletTx wtx; + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false)) + return false; + + if (nChangePosRet != -1) + tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]); + + // Add new txins (keeping original txin scriptSig/order) + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + bool found = false; + BOOST_FOREACH(const CTxIn& origTxIn, tx.vin) + { + if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n) + { + found = true; + break; + } + } + if (!found) + tx.vin.push_back(txin); + } + + return true; } -bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl) +bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign) { CAmount nValue = 0; unsigned int nSubtractFeeFromAmount = 0; @@ -1890,23 +1974,43 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, // Sign int nIn = 0; + CTransaction txNewConst(txNew); BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*this, *coin.first, txNew, nIn++)) + { + bool signSuccess; + const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; + CScript& scriptSigRes = txNew.vin[nIn].scriptSig; + if (sign) + signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes); + else + signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes); + + if (!signSuccess) { strFailReason = _("Signing transaction failed"); return false; } + nIn++; + } + + unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + + // Remove scriptSigs if we used dummy signatures for fee calculation + if (!sign) { + BOOST_FOREACH (CTxIn& vin, txNew.vin) + vin.scriptSig = CScript(); + } // Embed the constructed transaction data in wtxNew. *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); // Limit size - unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_STANDARD_TX_SIZE) { strFailReason = _("Transaction too large"); return false; } + dPriority = wtxNew.ComputePriority(dPriority, nBytes); // Can we complete this as a free transaction? @@ -2480,6 +2584,17 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx) } } +void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script) +{ + boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this)); + CPubKey pubkey; + if (!rKey->GetReservedKey(pubkey)) + return; + + script = rKey; + script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; +} + void CWallet::LockCoin(COutPoint& output) { AssertLockHeld(cs_wallet); // setLockedCoins diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9f3f08d117..f36c98e9fc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -28,6 +28,8 @@ #include <utility> #include <vector> +#include <boost/shared_ptr.hpp> + /** * Settings */ @@ -625,8 +627,9 @@ public: CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; - bool CreateTransaction(const std::vector<CRecipient>& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason); + bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); static CFeeRate minTxFee; @@ -679,6 +682,13 @@ public: } } + void GetScriptForMining(boost::shared_ptr<CReserveScript> &script); + void ResetRequestCount(const uint256 &hash) + { + LOCK(cs_wallet); + mapRequestCount[hash] = 0; + }; + unsigned int GetKeyPoolSize() { AssertLockHeld(cs_wallet); // setKeyPool @@ -734,7 +744,7 @@ public: }; /** A key allocated from the key pool. */ -class CReserveKey +class CReserveKey : public CReserveScript { protected: CWallet* pwallet; @@ -755,6 +765,7 @@ public: void ReturnKey(); bool GetReservedKey(CPubKey &pubkey); void KeepKey(); + void KeepScript() { KeepKey(); } }; |