aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/chain.cpp7
-rw-r--r--src/chain.h3
-rw-r--r--src/init.cpp24
-rw-r--r--src/main.cpp206
-rw-r--r--src/main.h13
-rw-r--r--src/net.cpp6
-rw-r--r--src/qt/bitcoingui.cpp9
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/forms/modaloverlay.ui4
-rw-r--r--src/qt/forms/sendcoinsdialog.ui24
-rw-r--r--src/qt/modaloverlay.cpp3
-rw-r--r--src/qt/sendcoinsdialog.cpp36
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/splashscreen.cpp12
-rw-r--r--src/qt/splashscreen.h5
-rw-r--r--src/qt/walletmodel.cpp16
-rw-r--r--src/qt/walletmodel.h11
-rw-r--r--src/rpc/blockchain.cpp12
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/misc.cpp2
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/test/bctest.py30
-rw-r--r--src/test/blockencodings_tests.cpp4
-rw-r--r--src/test/data/tx_invalid.json26
-rw-r--r--src/test/data/tx_valid.json23
-rw-r--r--src/test/mempool_tests.cpp30
-rw-r--r--src/test/miner_tests.cpp3
-rw-r--r--src/test/policyestimator_tests.cpp11
-rw-r--r--src/test/versionbits_tests.cpp104
-rw-r--r--src/txmempool.cpp35
-rw-r--r--src/txmempool.h14
-rw-r--r--src/versionbits.cpp35
-rw-r--r--src/versionbits.h4
-rw-r--r--src/wallet/coincontrol.h (renamed from src/coincontrol.h)9
-rw-r--r--src/wallet/rpcdump.cpp422
-rw-r--r--src/wallet/rpcwallet.cpp21
-rw-r--r--src/wallet/wallet.cpp25
-rw-r--r--src/wallet/wallet.h4
41 files changed, 1006 insertions, 203 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 54abd2ce46..5a5e3abcfa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -87,7 +87,6 @@ BITCOIN_CORE_H = \
checkpoints.h \
checkqueue.h \
clientversion.h \
- coincontrol.h \
coins.h \
compat.h \
compat/byteswap.h \
@@ -147,6 +146,7 @@ BITCOIN_CORE_H = \
utiltime.h \
validationinterface.h \
versionbits.h \
+ wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
wallet/rpcwallet.h \
diff --git a/src/chain.cpp b/src/chain.cpp
index 77e924e703..1e611906d1 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -61,6 +61,13 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
return pindex;
}
+CBlockIndex* CChain::FindLatestBefore(int64_t nTime) const
+{
+ std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
+ [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTime() < time; });
+ return (lower == vChain.end() ? NULL : *lower);
+}
+
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }
diff --git a/src/chain.h b/src/chain.h
index e2f8c56522..46a16a3061 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -459,6 +459,9 @@ public:
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
+
+ /** Find the most recent block with timestamp lower than the given. */
+ CBlockIndex* FindLatestBefore(int64_t nTime) const;
};
#endif // BITCOIN_CHAIN_H
diff --git a/src/init.cpp b/src/init.cpp
index 2b1fbed072..efaf821f4f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -207,6 +207,7 @@ void Shutdown()
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
+ DumpMempool();
if (fFeeEstimatesInitialized)
{
@@ -359,13 +360,13 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));
strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME));
strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6"));
- strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s)"));
+ strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections"));
strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"));
strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP));
- strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)"));
+ strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect)"));
strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address"));
strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED));
- strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"));
+ strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect)"));
strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION));
strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS));
strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER));
@@ -659,6 +660,8 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
LogPrintf("Stopping after block import\n");
StartShutdown();
}
+
+ LoadMempool();
}
/** Sanity checks
@@ -960,8 +963,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
::minRelayTxFee = CFeeRate(n);
}
- fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !Params().RequireStandard());
- if (Params().RequireStandard() && !fRequireStandard)
+ fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
+ if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
@@ -996,7 +999,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!mapMultiArgs["-bip9params"].empty()) {
// Allow overriding BIP9 parameters for testing
- if (!Params().MineBlocksOnDemand()) {
+ if (!chainparams.MineBlocksOnDemand()) {
return InitError("BIP9 parameters may only be overridden on regtest.");
}
const vector<string>& deployments = mapMultiArgs["-bip9params"];
@@ -1447,7 +1450,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- if (Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
+ if (chainparams.GetConsensus().vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) {
// Only advertize witness capabilities if they have a reasonable start time.
// This allows us to have the code merged without a defined softfork, by setting its
// end time to 0.
@@ -1493,6 +1496,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
}
+#ifdef ENABLE_WALLET
+ // Add wallet transactions that aren't already in a block to mempool
+ // Do this here as mempool requires genesis block to be loaded
+ if (pwalletMain)
+ pwalletMain->ReacceptWalletTransactions();
+#endif
+
// ********************************************************* Step 11: start node
//// debug print
diff --git a/src/main.cpp b/src/main.cpp
index 1777717cd9..11abc0d175 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1135,7 +1135,7 @@ std::string FormatStateMessage(const CValidationState &state)
}
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree,
- bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
+ bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
std::vector<uint256>& vHashTxnToUncache)
{
const uint256 hash = tx.GetHash();
@@ -1308,7 +1308,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
+ CTxMemPoolEntry entry(tx, nFees, nAcceptTime, dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
unsigned int nSize = entry.GetTxSize();
// Check that the transaction doesn't have an excessive number of
@@ -1572,11 +1572,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return true;
}
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
- bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
+bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
std::vector<uint256> vHashTxToUncache;
- bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
+ bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
if (!res) {
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
pcoinsTip->Uncache(hashTx);
@@ -1584,7 +1584,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return res;
}
-/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
+{
+ return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), fOverrideMempoolLimit, nAbsurdFee);
+}
+
+/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
@@ -2768,10 +2774,9 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
std::vector<uint256> vHashUpdate;
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
// ignore validation errors in resurrected transactions
- list<CTransaction> removed;
CValidationState stateDummy;
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
- mempool.removeRecursive(tx, removed);
+ mempool.removeRecursive(tx);
} else if (mempool.exists(tx.GetHash())) {
vHashUpdate.push_back(tx.GetHash());
}
@@ -2804,7 +2809,7 @@ static int64_t nTimePostConnect = 0;
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
-bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::list<CTransaction> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>> &txChanged)
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::vector<std::shared_ptr<const CTransaction>> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>> &txChanged)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
@@ -2840,7 +2845,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool.;
- mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload());
+ mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, &txConflicted, !IsInitialBlockDownload());
// Update chainActive & related variables.
UpdateTip(pindexNew, chainparams);
@@ -2927,7 +2932,7 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::list<CTransaction>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>>& txChanged)
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::vector<std::shared_ptr<const CTransaction>>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>>& txChanged)
{
AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -3006,9 +3011,8 @@ static void NotifyHeaderTip() {
CBlockIndex* pindexHeader = NULL;
{
LOCK(cs_main);
- if (!setBlockIndexCandidates.empty()) {
- pindexHeader = *setBlockIndexCandidates.rbegin();
- }
+ pindexHeader = pindexBestHeader;
+
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
fInitialBlockDownload = IsInitialBlockDownload();
@@ -3039,7 +3043,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break;
const CBlockIndex *pindexFork;
- std::list<CTransaction> txConflicted;
+ std::vector<std::shared_ptr<const CTransaction>> txConflicted;
bool fInitialDownload;
{
LOCK(cs_main);
@@ -3070,9 +3074,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
// throw all transactions though the signal-interface
// while _not_ holding the cs_main lock
- BOOST_FOREACH(const CTransaction &tx, txConflicted)
+ for(std::shared_ptr<const CTransaction> tx : txConflicted)
{
- GetMainSignals().SyncTransaction(tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
+ GetMainSignals().SyncTransaction(*tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
}
// ... and about transactions that got confirmed:
for(unsigned int i = 0; i < txChanged.size(); i++)
@@ -5443,6 +5447,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
BlockTransactionsRequest req;
vRecv >> req;
+ LOCK(cs_main);
+
BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
@@ -5829,29 +5835,34 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
BlockTransactions resp;
vRecv >> resp;
- LOCK(cs_main);
+ CBlock block;
+ bool fBlockRead = false;
+ {
+ LOCK(cs_main);
- map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
- if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
- it->second.first != pfrom->GetId()) {
- LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
- return true;
- }
+ map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator it = mapBlocksInFlight.find(resp.blockhash);
+ if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock ||
+ it->second.first != pfrom->GetId()) {
+ LogPrint("net", "Peer %d sent us block transactions for block we weren't expecting\n", pfrom->id);
+ return true;
+ }
- PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
- CBlock block;
- ReadStatus status = partialBlock.FillBlock(block, resp.txn);
- if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
- Misbehaving(pfrom->GetId(), 100);
- LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
- return true;
- } else if (status == READ_STATUS_FAILED) {
- // Might have collided, fall back to getdata now :(
- std::vector<CInv> invs;
- invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
- pfrom->PushMessage(NetMsgType::GETDATA, invs);
- } else {
+ PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
+ ReadStatus status = partialBlock.FillBlock(block, resp.txn);
+ if (status == READ_STATUS_INVALID) {
+ MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist
+ Misbehaving(pfrom->GetId(), 100);
+ LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->id);
+ return true;
+ } else if (status == READ_STATUS_FAILED) {
+ // Might have collided, fall back to getdata now :(
+ std::vector<CInv> invs;
+ invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
+ pfrom->PushMessage(NetMsgType::GETDATA, invs);
+ } else
+ fBlockRead = true;
+ } // Don't hold cs_main when we call into ProcessNewBlock
+ if (fBlockRead) {
CValidationState state;
ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL);
int nDoS;
@@ -6918,6 +6929,125 @@ ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::D
return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
}
+int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
+{
+ LOCK(cs_main);
+ return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache);
+}
+
+static const uint64_t MEMPOOL_DUMP_VERSION = 1;
+
+bool LoadMempool(void)
+{
+ int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
+ FILE* filestr = fopen((GetDataDir() / "mempool.dat").string().c_str(), "r");
+ CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
+ return false;
+ }
+
+ int64_t count = 0;
+ int64_t skipped = 0;
+ int64_t failed = 0;
+ int64_t nNow = GetTime();
+
+ try {
+ uint64_t version;
+ file >> version;
+ if (version != MEMPOOL_DUMP_VERSION) {
+ return false;
+ }
+ uint64_t num;
+ file >> num;
+ double prioritydummy = 0;
+ while (num--) {
+ CTransaction tx;
+ int64_t nTime;
+ int64_t nFeeDelta;
+ file >> tx;
+ file >> nTime;
+ file >> nFeeDelta;
+
+ CAmount amountdelta = nFeeDelta;
+ if (amountdelta) {
+ mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), prioritydummy, amountdelta);
+ }
+ CValidationState state;
+ if (nTime + nExpiryTimeout > nNow) {
+ LOCK(cs_main);
+ AcceptToMemoryPoolWithTime(mempool, state, tx, true, NULL, nTime);
+ if (state.IsValid()) {
+ ++count;
+ } else {
+ ++failed;
+ }
+ } else {
+ ++skipped;
+ }
+ }
+ std::map<uint256, CAmount> mapDeltas;
+ file >> mapDeltas;
+
+ for (const auto& i : mapDeltas) {
+ mempool.PrioritiseTransaction(i.first, i.first.ToString(), prioritydummy, i.second);
+ }
+ } catch (const std::exception& e) {
+ LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
+ return false;
+ }
+
+ LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped);
+ return true;
+}
+
+void DumpMempool(void)
+{
+ int64_t start = GetTimeMicros();
+
+ std::map<uint256, CAmount> mapDeltas;
+ std::vector<TxMempoolInfo> vinfo;
+
+ {
+ LOCK(mempool.cs);
+ for (const auto &i : mempool.mapDeltas) {
+ mapDeltas[i.first] = i.second.first;
+ }
+ vinfo = mempool.infoAll();
+ }
+
+ int64_t mid = GetTimeMicros();
+
+ try {
+ FILE* filestr = fopen((GetDataDir() / "mempool.dat.new").string().c_str(), "w");
+ if (!filestr) {
+ return;
+ }
+
+ CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
+
+ uint64_t version = MEMPOOL_DUMP_VERSION;
+ file << version;
+
+ file << (uint64_t)vinfo.size();
+ for (const auto& i : vinfo) {
+ file << *(i.tx);
+ file << (int64_t)i.nTime;
+ file << (int64_t)i.nFeeDelta;
+ mapDeltas.erase(i.tx->GetHash());
+ }
+
+ file << mapDeltas;
+ FileCommit(file.Get());
+ file.fclose();
+ RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
+ int64_t last = GetTimeMicros();
+ LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001);
+ } catch (const std::exception& e) {
+ LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
+ }
+}
+
class CMainCleanup
{
public:
diff --git a/src/main.h b/src/main.h
index e91f6e46fe..4c6af95142 100644
--- a/src/main.h
+++ b/src/main.h
@@ -291,13 +291,18 @@ void PruneAndFlush();
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
+/** (try to) add transaction to memory pool with a specified acceptance time **/
+bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
+
/** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
-
+/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
+int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
@@ -528,6 +533,12 @@ static const unsigned int REJECT_ALREADY_KNOWN = 0x101;
/** Transaction conflicts with a transaction already known */
static const unsigned int REJECT_CONFLICT = 0x102;
+/** Dump the mempool to disk. */
+void DumpMempool();
+
+/** Load the mempool from disk. */
+bool LoadMempool();
+
// The following things handle network-processing logic
// (and should be moved to a separate file)
diff --git a/src/net.cpp b/src/net.cpp
index 1bca168d1d..48ba9588d9 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -970,7 +970,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
CAddress addr;
int nInbound = 0;
int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
- assert(nMaxInbound > 0);
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@@ -2138,8 +2137,9 @@ bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, st
// Initiate outbound connections from -addnode
threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "addcon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenAddedConnections, this))));
- // Initiate outbound connections
- threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "opencon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenConnections, this))));
+ // Initiate outbound connections unless connect=0
+ if (!mapArgs.count("-connect") || mapMultiArgs["-connect"].size() != 1 || mapMultiArgs["-connect"][0] != "0")
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "opencon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenConnections, this))));
// Process messages
threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "msghand", boost::function<void()>(boost::bind(&CConnman::ThreadMessageHandler, this))));
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index af767aa6c6..ee5102c4f9 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -717,13 +717,10 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
{
if (modalOverlay)
{
- if (header) {
- /* use clientmodels getHeaderTipHeight and getHeaderTipTime because the NotifyHeaderTip signal does not fire when updating the best header */
- modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime()));
- }
- else {
+ if (header)
+ modalOverlay->setKnownBestHeight(count, blockDate);
+ else
modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
- }
}
if (!clientModel)
return;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 86fd4ebd65..1a1671f0ee 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -13,7 +13,7 @@
#include "txmempool.h"
#include "walletmodel.h"
-#include "coincontrol.h"
+#include "wallet/coincontrol.h"
#include "init.h"
#include "main.h" // For minRelayTxFee
#include "wallet/wallet.h"
diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui
index 73223735f5..27998f90c5 100644
--- a/src/qt/forms/modaloverlay.ui
+++ b/src/qt/forms/modaloverlay.ui
@@ -219,7 +219,7 @@ QLabel { color: rgb(40,40,40); }</string>
<item row="0" column="1">
<widget class="QLabel" name="numberOfBlocksLeft">
<property name="text">
- <string>unknown...</string>
+ <string>Unknown...</string>
</property>
</widget>
</item>
@@ -245,7 +245,7 @@ QLabel { color: rgb(40,40,40); }</string>
</sizepolicy>
</property>
<property name="text">
- <string>unknown...</string>
+ <string>Unknown...</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 06e09074d1..33db9f8938 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -411,7 +411,7 @@
</property>
</widget>
</item>
- </layout>
+ </layout>
</item>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl4">
@@ -1031,7 +1031,7 @@
<item>
<widget class="QLabel" name="labelSmartFee3">
<property name="text">
- <string>Confirmation time:</string>
+ <string>Confirmation time target:</string>
</property>
<property name="margin">
<number>2</number>
@@ -1096,6 +1096,26 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="confirmationTargetLabel">
+ <property name="text">
+ <string>(count)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index ae0d8f5f63..1a843a07ac 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -132,7 +132,8 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri
if (estimateNumHeadersLeft < 24 && hasBestHeader) {
ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count));
} else {
- ui->expectedTimeLeft->setText(tr("Unknown. Syncing Headers..."));
+ ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight));
+ ui->expectedTimeLeft->setText(tr("Unknown..."));
}
}
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 4b2ba7d624..57b2179435 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -16,7 +16,7 @@
#include "walletmodel.h"
#include "base58.h"
-#include "coincontrol.h"
+#include "wallet/coincontrol.h"
#include "main.h" // mempool and minRelayTxFee
#include "ui_interface.h"
#include "txmempool.h"
@@ -110,7 +110,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
- ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
@@ -172,6 +171,13 @@ void SendCoinsDialog::setModel(WalletModel *_model)
updateMinFeeLabel();
updateSmartFeeLabel();
updateGlobalFeeVariables();
+
+ // set the smartfee-sliders default value (wallets default conf.target or last stored value)
+ QSettings settings;
+ if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
+ ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 1);
+ else
+ ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
}
}
@@ -229,10 +235,17 @@ void SendCoinsDialog::on_sendButton_clicked()
// prepare transaction for getting txFee earlier
WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
- if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
- prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
+
+ // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
+ CCoinControl ctrl;
+ if (model->getOptionsModel()->getCoinControlFeatures())
+ ctrl = *CoinControlDialog::coinControl;
+ if (ui->radioSmartFee->isChecked())
+ ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 1;
else
- prepareStatus = model->prepareTransaction(currentTransaction);
+ ctrl.nConfirmTarget = 0;
+
+ prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(prepareStatus,
@@ -521,7 +534,7 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::TransactionCommitFailed:
- msgParams.first = tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
+ msgParams.first = tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed);
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
@@ -576,6 +589,7 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked());
ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
@@ -587,15 +601,17 @@ void SendCoinsDialog::updateGlobalFeeVariables()
{
if (ui->radioSmartFee->isChecked())
{
- nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value();
+ int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 1;
payTxFee = CFeeRate(0);
// set nMinimumTotalFee to 0 to not accidentally pay a custom fee
CoinControlDialog::coinControl->nMinimumTotalFee = 0;
+
+ // show the estimated reuquired time for confirmation
+ ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget*600)+" / "+tr("%n block(s)", "", nConfirmTarget));
}
else
{
- nTxConfirmTarget = defaultConfirmTarget;
payTxFee = CFeeRate(ui->customFee->value());
// if user has selected to set a minimum absolute fee, pass the value to coincontrol
@@ -630,7 +646,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
if(!model || !model->getOptionsModel())
return;
- int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value();
+ int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 1;
int estimateFoundAtBlocks = nBlocksToConfirm;
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
@@ -701,6 +717,8 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
if (!checked && model) // coin control features disabled
CoinControlDialog::coinControl->SetNull();
+ // make sure we set back the confirmation target
+ updateGlobalFeeVariables();
coinControlUpdateLabels();
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 83dac0bd11..b0df495a98 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -26,8 +26,6 @@ QT_BEGIN_NAMESPACE
class QUrl;
QT_END_NAMESPACE
-const int defaultConfirmTarget = 25;
-
/** Dialog for sending bitcoins */
class SendCoinsDialog : public QDialog
{
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 4061909b71..3e42f3a7b0 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -142,7 +142,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!pwalletMain->GetKey(keyID, key))
+ if (!model->getPrivKey(keyID, key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index e36d86fddd..cd27385653 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -164,9 +164,10 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
}
#ifdef ENABLE_WALLET
-static void ConnectWallet(SplashScreen *splash, CWallet* wallet)
+void SplashScreen::ConnectWallet(CWallet* wallet)
{
- wallet->ShowProgress.connect(boost::bind(ShowProgress, splash, _1, _2));
+ wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
+ connectedWallets.push_back(wallet);
}
#endif
@@ -176,7 +177,7 @@ void SplashScreen::subscribeToCoreSignals()
uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1));
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
#ifdef ENABLE_WALLET
- uiInterface.LoadWallet.connect(boost::bind(ConnectWallet, this, _1));
+ uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1));
#endif
}
@@ -186,8 +187,9 @@ void SplashScreen::unsubscribeFromCoreSignals()
uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1));
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
#ifdef ENABLE_WALLET
- if(pwalletMain)
- pwalletMain->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ Q_FOREACH(CWallet* const & pwallet, connectedWallets) {
+ pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
+ }
#endif
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 821f39db1c..d1727b66c9 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -7,6 +7,7 @@
#include <QSplashScreen>
+class CWallet;
class NetworkStyle;
/** Class for the splashscreen with information of the running client.
@@ -39,11 +40,15 @@ private:
void subscribeToCoreSignals();
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
+ /** Connect wallet signals to splash screen */
+ void ConnectWallet(CWallet*);
QPixmap pixmap;
QString curMessage;
QColor curColor;
int curAlignment;
+
+ QList<CWallet*> connectedWallets;
};
#endif // BITCOIN_QT_SPLASHSCREEN_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index c8a2cb37ec..3490d1c1cc 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -5,6 +5,7 @@
#include "walletmodel.h"
#include "addresstablemodel.h"
+#include "consensus/validation.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "paymentserver.h"
@@ -328,8 +329,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
}
CReserveKey *keyChange = transaction.getPossibleKeyChange();
- if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get()))
- return TransactionCommitFailed;
+ CValidationState state;
+ if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state))
+ return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason()));
CTransaction* t = (CTransaction*)newTx;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -563,6 +565,11 @@ bool WalletModel::havePrivKey(const CKeyID &address) const
return wallet->HaveKey(address);
}
+bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const
+{
+ return wallet->GetKey(address, vchPrivKeyOut);
+}
+
// returns a list of COutputs from COutPoints
void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
{
@@ -693,3 +700,8 @@ bool WalletModel::hdEnabled() const
{
return wallet->IsHDEnabled();
}
+
+int WalletModel::getDefaultConfirmTarget() const
+{
+ return nTxConfirmTarget;
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b105c6d991..6a5670e378 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -144,9 +144,13 @@ public:
// Return status record for SendCoins, contains error id + information
struct SendCoinsReturn
{
- SendCoinsReturn(StatusCode _status = OK):
- status(_status) {}
+ SendCoinsReturn(StatusCode _status = OK, QString _reasonCommitFailed = "")
+ : status(_status),
+ reasonCommitFailed(_reasonCommitFailed)
+ {
+ }
StatusCode status;
+ QString reasonCommitFailed;
};
// prepare transaction for getting txfee before sending coins
@@ -187,6 +191,7 @@ public:
bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
bool havePrivKey(const CKeyID &address) const;
+ bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const;
void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
bool isSpent(const COutPoint& outpoint) const;
void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const;
@@ -206,6 +211,8 @@ public:
bool hdEnabled() const;
+ int getDefaultConfirmTarget() const;
+
private:
CWallet *wallet;
bool fHaveWatchOnly;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 0c827a1e38..8caea14adb 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -151,7 +151,7 @@ UniValue getblockcount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getblockcount\n"
- "\nReturns the number of blocks in the longest block chain.\n"
+ "\nReturns the number of blocks in the longest blockchain.\n"
"\nResult:\n"
"n (numeric) The current block count\n"
"\nExamples:\n"
@@ -168,7 +168,7 @@ UniValue getbestblockhash(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getbestblockhash\n"
- "\nReturns the hash of the best (tip) block in the longest block chain.\n"
+ "\nReturns the hash of the best (tip) block in the longest blockchain.\n"
"\nResult\n"
"\"hex\" (string) the block hash hex encoded\n"
"\nExamples\n"
@@ -863,7 +863,7 @@ UniValue gettxout(const JSONRPCRequest& request)
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. n (numeric, required) vout number\n"
- "3. includemempool (boolean, optional) Whether to include the mem pool\n"
+ "3. includemempool (boolean, optional) Whether to include the mempool\n"
"\nResult:\n"
"{\n"
" \"bestblock\" : \"hash\", (string) the block hash\n"
@@ -1009,6 +1009,7 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
}
rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime));
rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout));
+ rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id)));
return rv;
}
@@ -1026,7 +1027,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw runtime_error(
"getblockchaininfo\n"
- "Returns an object containing various state info regarding block chain processing.\n"
+ "Returns an object containing various state info regarding blockchain processing.\n"
"\nResult:\n"
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
@@ -1053,7 +1054,8 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
- " \"timeout\": xx (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
+ " \"since\": xx (numeric) height of the first block to which the status applies\n"
" }\n"
" }\n"
"}\n"
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index c14d9d6747..8370a0f43e 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -95,6 +95,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "importaddress", 2 },
{ "importaddress", 3 },
{ "importpubkey", 2 },
+ { "importmulti", 0 },
+ { "importmulti", 1 },
{ "verifychain", 0 },
{ "verifychain", 1 },
{ "keypoolrefill", 0 },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index d509dd691f..be0776ea22 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -230,7 +230,7 @@ UniValue getmininginfo(const JSONRPCRequest& request)
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n"
" \"networkhashps\": nnn, (numeric) The network hashes per second\n"
- " \"pooledtx\": n (numeric) The size of the mem pool\n"
+ " \"pooledtx\": n (numeric) The size of the mempool\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
"}\n"
"\nExamples:\n"
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 30db06dd32..3193985803 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -57,7 +57,7 @@ UniValue getinfo(const JSONRPCRequest& request)
" \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"testnet\": true|false, (boolean) if the server is using testnet or not\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix 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 set in " + CURRENCY_UNIT + "/kB\n"
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 836cf9ee35..0e17ddc130 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -890,7 +890,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
- // Drop the signature, since there's no way for a signature to sign itself
+ // Drop the signature in pre-segwit scripts but not segwit scripts
if (sigversion == SIGVERSION_BASE) {
scriptCode.FindAndDelete(CScript(vchSig));
}
@@ -951,7 +951,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
- // Drop the signatures, since there's no way for a signature to sign itself
+ // Drop the signature in pre-segwit scripts but not segwit scripts
for (int k = 0; k < nSigsCount; k++)
{
valtype& vchSig = stacktop(-isig-k);
diff --git a/src/test/bctest.py b/src/test/bctest.py
index d801415c70..eab4fb734a 100644
--- a/src/test/bctest.py
+++ b/src/test/bctest.py
@@ -6,6 +6,15 @@ import subprocess
import os
import json
import sys
+import binascii
+
+def parse_output(a, fmt):
+ if fmt == 'json': # json: compare parsed data
+ return json.loads(a)
+ elif fmt == 'hex': # hex: parse and compare binary data
+ return binascii.a2b_hex(a.strip())
+ else:
+ raise NotImplementedError("Don't know how to compare %s" % fmt)
def bctest(testDir, testObj, exeext):
@@ -23,6 +32,7 @@ def bctest(testDir, testObj, exeext):
outputData = None
if "output_cmp" in testObj:
outputFn = testObj['output_cmp']
+ outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare)
outputData = open(testDir + "/" + outputFn).read()
if not outputData:
print("Output data missing for " + outputFn)
@@ -34,9 +44,23 @@ def bctest(testDir, testObj, exeext):
print("OSError, Failed to execute " + execprog)
sys.exit(1)
- if outputData and (outs[0] != outputData):
- print("Output data mismatch for " + outputFn)
- sys.exit(1)
+ if outputData:
+ try:
+ a_parsed = parse_output(outs[0], outputType)
+ except Exception as e:
+ print('Error parsing command output as %s: %s' % (outputType,e))
+ sys.exit(1)
+ try:
+ b_parsed = parse_output(outputData, outputType)
+ except Exception as e:
+ print('Error parsing expected output %s as %s: %s' % (outputFn,outputType,e))
+ sys.exit(1)
+ if a_parsed != b_parsed:
+ print("Output data mismatch for " + outputFn + " (format " + outputType + ")")
+ sys.exit(1)
+ if outs[0] != outputData:
+ print("Output formatting mismatch for " + outputFn + " (format " + outputType + ")")
+ sys.exit(1)
wantRC = 0
if "return_code" in testObj:
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 7530b013bd..b0d9184816 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -80,8 +80,8 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
- std::list<CTransaction> removed;
- pool.removeRecursive(block.vtx[2], removed);
+ std::vector<std::shared_ptr<const CTransaction>> removed;
+ pool.removeRecursive(block.vtx[2], &removed);
BOOST_CHECK_EQUAL(removed.size(), 1);
CBlock block2;
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index f8baee0577..f7d9e1847f 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -314,5 +314,31 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x21 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff", 1000]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"],
+["FindAndDelete tests"],
+["This is a test of FindAndDelete. The first tx is a spend of normal scriptPubKey and the second tx is a spend of bare P2WSH."],
+["The redeemScript/witnessScript is CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>."],
+["The signature is <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01> <pubkey>,"],
+["where the pubkey is obtained through key recovery with sig and the wrong sighash."],
+["This is to show that FindAndDelete is applied only to non-segwit scripts"],
+["To show that the tests are 'correctly wrong', they should pass by modifying OP_CHECKSIG under interpreter.cpp"],
+["by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE)"],
+["Non-segwit: wrong sighash (without FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
+"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: wrong sighash (with FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
+"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9d7ed6e161f0e255c10bbfcca0128a9e2035c2c8da58899c54d22d3a31afdef4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"],
+["This is multisig version of the FindAndDelete tests"],
+["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
+["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
+["Signature is 0 <sig1> <sig2> 2 <key1> <key2>"],
+["Should pass by replacing (sigversion == SIGVERSION_BASE) with (sigversion != SIGVERSION_BASE) under OP_CHECKMULTISIG"],
+["Non-segwit: wrong sighash (without FindAndDelete) = 4bc6a53e8e16ef508c19e38bba08831daba85228b0211f323d4cb0999cf2a5e8"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
+"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: wrong sighash (with FindAndDelete) = 17c50ec2181ecdfdc85ca081174b248199ba81fff730794d4f69b8ec031f2dce"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
+"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 1ea70135b4..2f299aa5fe 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -487,5 +487,28 @@
[[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]],
"0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "P2SH,WITNESS"],
+["FindAndDelete tests"],
+["This is a test of FindAndDelete. The first tx is a spend of normal P2SH and the second tx is a spend of bare P2WSH."],
+["The redeemScript/witnessScript is CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>."],
+["The signature is <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01> <pubkey>,"],
+["where the pubkey is obtained through key recovery with sig and correct sighash."],
+["This is to show that FindAndDelete is applied only to non-segwit scripts"],
+["Non-segwit: correct sighash (with FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]],
+"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"],
+[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]],
+"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"],
+["This is multisig version of the FindAndDelete tests"],
+["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"],
+["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"],
+["Signature is 0 <sig1> <sig2> 2 <key1> <key2>"],
+["Non-segwit: correct sighash (with FindAndDelete) = 1d50f00ba4db2917b903b0ec5002e017343bb38876398c9510570f5dce099295"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]],
+"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"],
+["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"],
+[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]],
+"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 033a50f94f..555d36faac 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -55,15 +55,15 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
CTxMemPool testPool(CFeeRate(0));
- std::list<CTransaction> removed;
+ std::vector<std::shared_ptr<const CTransaction>> removed;
// Nothing in pool, remove should do nothing:
- testPool.removeRecursive(txParent, removed);
+ testPool.removeRecursive(txParent, &removed);
BOOST_CHECK_EQUAL(removed.size(), 0);
// Just the parent:
testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent));
- testPool.removeRecursive(txParent, removed);
+ testPool.removeRecursive(txParent, &removed);
BOOST_CHECK_EQUAL(removed.size(), 1);
removed.clear();
@@ -75,16 +75,16 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i]));
}
// Remove Child[0], GrandChild[0] should be removed:
- testPool.removeRecursive(txChild[0], removed);
+ testPool.removeRecursive(txChild[0], &removed);
BOOST_CHECK_EQUAL(removed.size(), 2);
removed.clear();
// ... make sure grandchild and child are gone:
- testPool.removeRecursive(txGrandChild[0], removed);
+ testPool.removeRecursive(txGrandChild[0], &removed);
BOOST_CHECK_EQUAL(removed.size(), 0);
- testPool.removeRecursive(txChild[0], removed);
+ testPool.removeRecursive(txChild[0], &removed);
BOOST_CHECK_EQUAL(removed.size(), 0);
// Remove parent, all children/grandchildren should go:
- testPool.removeRecursive(txParent, removed);
+ testPool.removeRecursive(txParent, &removed);
BOOST_CHECK_EQUAL(removed.size(), 5);
BOOST_CHECK_EQUAL(testPool.size(), 0);
removed.clear();
@@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
}
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
// put into the mempool (maybe because it is non-standard):
- testPool.removeRecursive(txParent, removed);
+ testPool.removeRecursive(txParent, &removed);
BOOST_CHECK_EQUAL(removed.size(), 6);
BOOST_CHECK_EQUAL(testPool.size(), 0);
removed.clear();
@@ -281,12 +281,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK_EQUAL(pool.size(), 10);
// Now try removing tx10 and verify the sort order returns to normal
- std::list<CTransaction> removed;
- pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx(), removed);
+ pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx());
CheckSort<descendant_score>(pool, snapshotOrder);
- pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), removed);
- pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), removed);
+ pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
+ pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
/* Now check the sort on the mining score index.
* Final order should be:
*
@@ -413,8 +412,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
/* after tx6 is mined, tx7 should move up in the sort */
std::vector<CTransaction> vtx;
vtx.push_back(tx6);
- std::list<CTransaction> dummy;
- pool.removeForBlock(vtx, 1, dummy, false);
+ pool.removeForBlock(vtx, 1, NULL, false);
sortedOrder.erase(sortedOrder.begin()+1);
sortedOrder.pop_back();
@@ -549,12 +547,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool));
std::vector<CTransaction> vtx;
- std::list<CTransaction> conflicts;
+ std::vector<std::shared_ptr<const CTransaction>> conflicts;
SetMockTime(42);
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
// ... we should keep the same min fee until we get a block
- pool.removeForBlock(vtx, 1, conflicts);
+ pool.removeForBlock(vtx, 1);
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2);
// ... then feerate should drop 1/2 each halflife
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index d6d7b5716e..a94979fd77 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -137,8 +137,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- std::list<CTransaction> dummy;
- mempool.removeRecursive(tx, dummy);
+ mempool.removeRecursive(tx);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx));
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 5c902387f1..f57c24270c 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -46,7 +46,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
for (unsigned int i = 0; i < 128; i++)
garbage.push_back('X');
CMutableTransaction tx;
- std::list<CTransaction> dummyConflicted;
tx.vin.resize(1);
tx.vin[0].scriptSig = garbage;
tx.vout.resize(1);
@@ -81,7 +80,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
txHashes[9-h].pop_back();
}
}
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
block.clear();
if (blocknum == 30) {
// At this point we should need to combine 5 buckets to get enough data points
@@ -125,7 +124,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Mine 50 more blocks with no transactions happening, estimates shouldn't change
// We haven't decayed the moving average enough so we still have enough data points in every bucket
while (blocknum < 250)
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
for (int i = 1; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
@@ -146,7 +145,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
txHashes[j].push_back(hash);
}
}
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
}
int answerFound;
@@ -167,7 +166,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
txHashes[j].pop_back();
}
}
- mpool.removeForBlock(block, 265, dummyConflicted);
+ mpool.removeForBlock(block, 265);
block.clear();
for (int i = 1; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
@@ -187,7 +186,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
block.push_back(*ptx);
}
}
- mpool.removeForBlock(block, ++blocknum, dummyConflicted);
+ mpool.removeForBlock(block, ++blocknum);
block.clear();
}
for (int i = 1; i < 10; i++) {
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index ffc0ff6f8e..784e796998 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2015 The Bitcoin Core developers
+// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -30,6 +30,7 @@ public:
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); }
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
#define CHECKERS 6
@@ -78,6 +79,16 @@ public:
return *this;
}
+ VersionBitsTester& TestStateSinceHeight(int height) {
+ for (int i = 0; i < CHECKERS; i++) {
+ if ((insecure_rand() & ((1 << i) - 1)) == 0) {
+ BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? NULL : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
+ }
+ }
+ num++;
+ return *this;
+ }
+
VersionBitsTester& TestDefined() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
@@ -137,53 +148,64 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
{
for (int i = 0; i < 64; i++) {
// DEFINED -> FAILED
- VersionBitsTester().TestDefined()
- .Mine(1, TestTime(1), 0x100).TestDefined()
- .Mine(11, TestTime(11), 0x100).TestDefined()
- .Mine(989, TestTime(989), 0x100).TestDefined()
- .Mine(999, TestTime(20000), 0x100).TestDefined()
- .Mine(1000, TestTime(20000), 0x100).TestFailed()
- .Mine(1999, TestTime(30001), 0x100).TestFailed()
- .Mine(2000, TestTime(30002), 0x100).TestFailed()
- .Mine(2001, TestTime(30003), 0x100).TestFailed()
- .Mine(2999, TestTime(30004), 0x100).TestFailed()
- .Mine(3000, TestTime(30005), 0x100).TestFailed()
+ VersionBitsTester().TestDefined().TestStateSinceHeight(0)
+ .Mine(1, TestTime(1), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(11, TestTime(11), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(989, TestTime(989), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(1999, TestTime(30001), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(1000)
+ .Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(1000)
// DEFINED -> STARTED -> FAILED
- .Reset().TestDefined()
- .Mine(1, TestTime(1), 0).TestDefined()
- .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined() // One second more and it would be defined
- .Mine(2000, TestTime(10000), 0x100).TestStarted() // So that's what happens the next period
- .Mine(2051, TestTime(10010), 0).TestStarted() // 51 old blocks
- .Mine(2950, TestTime(10020), 0x100).TestStarted() // 899 new blocks
- .Mine(3000, TestTime(20000), 0).TestFailed() // 50 old blocks (so 899 out of the past 1000)
- .Mine(4000, TestTime(20010), 0x100).TestFailed()
+ .Reset().TestDefined().TestStateSinceHeight(0)
+ .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(10000) - 1, 0x100).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
+ .Mine(2000, TestTime(10000), 0x100).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
+ .Mine(2051, TestTime(10010), 0).TestStarted().TestStateSinceHeight(2000) // 51 old blocks
+ .Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000) // 899 new blocks
+ .Mine(3000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(3000) // 50 old blocks (so 899 out of the past 1000)
+ .Mine(4000, TestTime(20010), 0x100).TestFailed().TestStateSinceHeight(3000)
// DEFINED -> STARTED -> FAILED while threshold reached
- .Reset().TestDefined()
- .Mine(1, TestTime(1), 0).TestDefined()
- .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined
- .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period
- .Mine(2999, TestTime(30000), 0x100).TestStarted() // 999 new blocks
- .Mine(3000, TestTime(30000), 0x100).TestFailed() // 1 new block (so 1000 out of the past 1000 are new)
- .Mine(3999, TestTime(30001), 0).TestFailed()
- .Mine(4000, TestTime(30002), 0).TestFailed()
- .Mine(14333, TestTime(30003), 0).TestFailed()
- .Mine(24000, TestTime(40000), 0).TestFailed()
+ .Reset().TestDefined().TestStateSinceHeight(0)
+ .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
+ .Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
+ .Mine(2999, TestTime(30000), 0x100).TestStarted().TestStateSinceHeight(2000) // 999 new blocks
+ .Mine(3000, TestTime(30000), 0x100).TestFailed().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
+ .Mine(3999, TestTime(30001), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(14333, TestTime(30003), 0).TestFailed().TestStateSinceHeight(3000)
+ .Mine(24000, TestTime(40000), 0).TestFailed().TestStateSinceHeight(3000)
// DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE
.Reset().TestDefined()
- .Mine(1, TestTime(1), 0).TestDefined()
- .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined
- .Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period
- .Mine(2050, TestTime(10010), 0x200).TestStarted() // 50 old blocks
- .Mine(2950, TestTime(10020), 0x100).TestStarted() // 900 new blocks
- .Mine(2999, TestTime(19999), 0x200).TestStarted() // 49 old blocks
- .Mine(3000, TestTime(29999), 0x200).TestLockedIn() // 1 old block (so 900 out of the past 1000)
- .Mine(3999, TestTime(30001), 0).TestLockedIn()
- .Mine(4000, TestTime(30002), 0).TestActive()
- .Mine(14333, TestTime(30003), 0).TestActive()
- .Mine(24000, TestTime(40000), 0).TestActive();
+ .Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
+ .Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
+ .Mine(2050, TestTime(10010), 0x200).TestStarted().TestStateSinceHeight(2000) // 50 old blocks
+ .Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000) // 900 new blocks
+ .Mine(2999, TestTime(19999), 0x200).TestStarted().TestStateSinceHeight(2000) // 49 old blocks
+ .Mine(3000, TestTime(29999), 0x200).TestLockedIn().TestStateSinceHeight(3000) // 1 old block (so 900 out of the past 1000)
+ .Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(30002), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(14333, TestTime(30003), 0).TestActive().TestStateSinceHeight(4000)
+ .Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)
+
+ // DEFINED multiple periods -> STARTED multiple periods -> FAILED
+ .Reset().TestDefined().TestStateSinceHeight(0)
+ .Mine(999, TestTime(999), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(1000, TestTime(1000), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(2000, TestTime(2000), 0).TestDefined().TestStateSinceHeight(0)
+ .Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(5000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
+ .Mine(6000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(6000)
+ .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000);
}
// Sanity checks of version bit deployments
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 0f1c166abc..313d33507f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -503,7 +503,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants
}
}
-void CTxMemPool::removeRecursive(const CTransaction &origTx, std::list<CTransaction>& removed)
+void CTxMemPool::removeRecursive(const CTransaction &origTx, std::vector<std::shared_ptr<const CTransaction>>* removed)
{
// Remove transaction from memory pool
{
@@ -530,8 +530,10 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, std::list<CTransact
BOOST_FOREACH(txiter it, txToRemove) {
CalculateDescendants(it, setAllRemoves);
}
- BOOST_FOREACH(txiter it, setAllRemoves) {
- removed.push_back(it->GetTx());
+ if (removed) {
+ BOOST_FOREACH(txiter it, setAllRemoves) {
+ removed->emplace_back(it->GetSharedTx());
+ }
}
RemoveStaged(setAllRemoves, false);
}
@@ -541,7 +543,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
{
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
LOCK(cs);
- list<CTransaction> transactionsToRemove;
+ setEntries txToRemove;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
@@ -549,16 +551,16 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
- transactionsToRemove.push_back(tx);
+ txToRemove.insert(it);
} else if (it->GetSpendsCoinbase()) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end())
continue;
const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash);
- if (nCheckFrequency != 0) assert(coins);
+ if (nCheckFrequency != 0) assert(coins);
if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) {
- transactionsToRemove.push_back(tx);
+ txToRemove.insert(it);
break;
}
}
@@ -567,13 +569,14 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
mapTx.modify(it, update_lock_points(lp));
}
}
- BOOST_FOREACH(const CTransaction& tx, transactionsToRemove) {
- list<CTransaction> removed;
- removeRecursive(tx, removed);
+ setEntries setAllRemoves;
+ for (txiter it : txToRemove) {
+ CalculateDescendants(it, setAllRemoves);
}
+ RemoveStaged(setAllRemoves, false);
}
-void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
+void CTxMemPool::removeConflicts(const CTransaction &tx, std::vector<std::shared_ptr<const CTransaction>>* removed)
{
// Remove transactions which depend on inputs of tx, recursively
LOCK(cs);
@@ -594,7 +597,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
*/
void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
- std::list<CTransaction>& conflicts, bool fCurrentEstimate)
+ std::vector<std::shared_ptr<const CTransaction>>* conflicts, bool fCurrentEstimate)
{
LOCK(cs);
std::vector<CTxMemPoolEntry> entries;
@@ -830,6 +833,10 @@ void CTxMemPool::queryHashes(vector<uint256>& vtxid)
}
}
+static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) {
+ return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize()), it->GetModifiedFee() - it->GetFee()};
+}
+
std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
{
LOCK(cs);
@@ -838,7 +845,7 @@ std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
std::vector<TxMempoolInfo> ret;
ret.reserve(mapTx.size());
for (auto it : iters) {
- ret.push_back(TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize())});
+ ret.push_back(GetInfo(it));
}
return ret;
@@ -859,7 +866,7 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
indexed_transaction_set::const_iterator i = mapTx.find(hash);
if (i == mapTx.end())
return TxMempoolInfo();
- return TxMempoolInfo{i->GetSharedTx(), i->GetTime(), CFeeRate(i->GetFee(), i->GetTxSize())};
+ return GetInfo(i);
}
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
diff --git a/src/txmempool.h b/src/txmempool.h
index 1763930ba0..9b0ca4655e 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -6,9 +6,12 @@
#ifndef BITCOIN_TXMEMPOOL_H
#define BITCOIN_TXMEMPOOL_H
-#include <list>
#include <memory>
#include <set>
+#include <map>
+#include <vector>
+#include <utility>
+#include <string>
#include "amount.h"
#include "coins.h"
@@ -326,6 +329,9 @@ struct TxMempoolInfo
/** Feerate of the transaction. */
CFeeRate feeRate;
+
+ /** The fee delta. */
+ int64_t nFeeDelta;
};
/**
@@ -521,11 +527,11 @@ public:
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true);
- void removeRecursive(const CTransaction &tx, std::list<CTransaction>& removed);
+ void removeRecursive(const CTransaction &tx, std::vector<std::shared_ptr<const CTransaction>>* removed = NULL);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
- void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
+ void removeConflicts(const CTransaction &tx, std::vector<std::shared_ptr<const CTransaction>>* removed = NULL);
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
- std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
+ std::vector<std::shared_ptr<const CTransaction>>* conflicts = NULL, bool fCurrentEstimate = true);
void clear();
void _clear(); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index bf32ae6627..d73f340510 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -105,6 +105,36 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
+int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
+{
+ const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
+
+ // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
+ if (initialState == THRESHOLD_DEFINED) {
+ return 0;
+ }
+
+ const int nPeriod = Period(params);
+
+ // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
+ // To ease understanding of the following height calculation, it helps to remember that
+ // right now pindexPrev points to the block prior to the block that we are computing for, thus:
+ // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
+ // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
+ // The parent of the genesis block is represented by NULL.
+ pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
+
+ const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
+
+ while (previousPeriodParent != NULL && GetStateFor(previousPeriodParent, params, cache) == initialState) {
+ pindexPrev = previousPeriodParent;
+ previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
+ }
+
+ // Adjust the result because right now we point to the parent block.
+ return pindexPrev->nHeight + 1;
+}
+
namespace
{
/**
@@ -137,6 +167,11 @@ ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]);
}
+int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
+{
+ return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]);
+}
+
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
return VersionBitsConditionChecker(pos).Mask(params);
diff --git a/src/versionbits.h b/src/versionbits.h
index ede2dcdda8..7a929266aa 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -51,8 +51,9 @@ protected:
virtual int Threshold(const Consensus::Params& params) const =0;
public:
- // Note that the function below takes a pindexPrev as input: they compute information for block B based on its parent.
+ // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
+ int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
};
struct VersionBitsCache
@@ -63,6 +64,7 @@ struct VersionBitsCache
};
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
+int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
#endif
diff --git a/src/coincontrol.h b/src/wallet/coincontrol.h
index e33adc4d2b..08d23688ff 100644
--- a/src/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_COINCONTROL_H
-#define BITCOIN_COINCONTROL_H
+#ifndef BITCOIN_WALLET_COINCONTROL_H
+#define BITCOIN_WALLET_COINCONTROL_H
#include "primitives/transaction.h"
@@ -22,6 +22,8 @@ public:
bool fOverrideFeeRate;
//! Feerate to use if overrideFeeRate is true
CFeeRate nFeeRate;
+ //! Override the default confirmation target, 0 = use default
+ int nConfirmTarget;
CCoinControl()
{
@@ -37,6 +39,7 @@ public:
nMinimumTotalFee = 0;
nFeeRate = CFeeRate(0);
fOverrideFeeRate = false;
+ nConfirmTarget = 0;
}
bool HasSelected() const
@@ -73,4 +76,4 @@ private:
std::set<COutPoint> setSelected;
};
-#endif // BITCOIN_COINCONTROL_H
+#endif // BITCOIN_WALLET_COINCONTROL_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 8a1bbd5684..b638810e9d 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -24,6 +24,7 @@
#include <univalue.h>
+#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
using namespace std;
@@ -637,3 +638,424 @@ UniValue dumpwallet(const JSONRPCRequest& request)
file.close();
return NullUniValue;
}
+
+
+UniValue processImport(const UniValue& data) {
+ try {
+ bool success = false;
+
+ // Required fields.
+ const UniValue& scriptPubKey = data["scriptPubKey"];
+
+ // Should have script or JSON with "address".
+ if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
+ }
+
+ // Optional fields.
+ const string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
+ const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
+ const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
+ const bool& internal = data.exists("internal") ? data["internal"].get_bool() : false;
+ const bool& watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
+ const string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
+ const int64_t& timestamp = data.exists("timestamp") && data["timestamp"].get_int64() > 1 ? data["timestamp"].get_int64() : 1;
+
+ bool isScript = scriptPubKey.getType() == UniValue::VSTR;
+ bool isP2SH = strRedeemScript.length() > 0;
+ const string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+
+ // Parse the output.
+ CScript script;
+ CBitcoinAddress address;
+
+ if (!isScript) {
+ address = CBitcoinAddress(output);
+ script = GetScriptForDestination(address.Get());
+ } else {
+ if (!IsHex(output)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey");
+ }
+
+ std::vector<unsigned char> vData(ParseHex(output));
+ script = CScript(vData.begin(), vData.end());
+ }
+
+ // Watchonly and private keys
+ if (watchOnly && keys.size()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
+ }
+
+ // Internal + Label
+ if (internal && data.exists("label")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
+ }
+
+ // Not having Internal + Script
+ if (!internal && isScript) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey");
+ }
+
+ // Keys / PubKeys size check.
+ if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
+ }
+
+ // Invalid P2SH redeemScript
+ if (isP2SH && !IsHex(strRedeemScript)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
+ }
+
+ // Process. //
+
+ // P2SH
+ if (isP2SH) {
+ // Import redeem script.
+ std::vector<unsigned char> vData(ParseHex(strRedeemScript));
+ CScript redeemScript = CScript(vData.begin(), vData.end());
+
+ // Invalid P2SH address
+ if (!script.IsPayToScriptHash()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ if (!pwalletMain->HaveCScript(redeemScript) && !pwalletMain->AddCScript(redeemScript)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
+ }
+
+ CBitcoinAddress redeemAddress = CBitcoinAddress(CScriptID(redeemScript));
+ CScript redeemDestination = GetScriptForDestination(redeemAddress.Get());
+
+ if (::IsMine(*pwalletMain, redeemDestination) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ // add to address book or update label
+ if (address.IsValid()) {
+ pwalletMain->SetAddressBook(address.Get(), label, "receive");
+ }
+
+ // Import private keys.
+ if (keys.size()) {
+ for (size_t i = 0; i < keys.size(); i++) {
+ const string& privkey = keys[i].get_str();
+
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(privkey);
+
+ if (!fGood) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+
+ CKey key = vchSecret.GetKey();
+
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ }
+
+ CPubKey pubkey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubkey));
+
+ CKeyID vchAddress = pubkey.GetID();
+ pwalletMain->MarkDirty();
+ pwalletMain->SetAddressBook(vchAddress, label, "receive");
+
+ if (pwalletMain->HaveKey(vchAddress)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
+ }
+
+ pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+
+ if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
+ }
+
+ if (timestamp < pwalletMain->nTimeFirstKey) {
+ pwalletMain->nTimeFirstKey = timestamp;
+ }
+ }
+ }
+
+ success = true;
+ } else {
+ // Import public keys.
+ if (pubKeys.size() && keys.size() == 0) {
+ const string& strPubKey = pubKeys[0].get_str();
+
+ if (!IsHex(strPubKey)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
+ }
+
+ std::vector<unsigned char> data(ParseHex(strPubKey));
+ CPubKey pubKey(data.begin(), data.end());
+
+ if (!pubKey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
+ }
+
+ CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
+
+ // Consistency check.
+ if (!isScript && !(pubKeyAddress.Get() == address.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+
+ // Consistency check.
+ if (isScript) {
+ CBitcoinAddress scriptAddress;
+ CTxDestination destination;
+
+ if (ExtractDestination(script, destination)) {
+ scriptAddress = CBitcoinAddress(destination);
+ if (!(scriptAddress.Get() == pubKeyAddress.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+ }
+ }
+
+ CScript pubKeyScript = GetScriptForDestination(pubKeyAddress.Get());
+
+ if (::IsMine(*pwalletMain, pubKeyScript) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ // add to address book or update label
+ if (pubKeyAddress.IsValid()) {
+ pwalletMain->SetAddressBook(pubKeyAddress.Get(), label, "receive");
+ }
+
+ // TODO Is this necessary?
+ CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
+
+ if (::IsMine(*pwalletMain, scriptRawPubKey) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ success = true;
+ }
+
+ // Import private keys.
+ if (keys.size()) {
+ const string& strPrivkey = keys[0].get_str();
+
+ // Checks.
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(strPrivkey);
+
+ if (!fGood) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+
+ CKey key = vchSecret.GetKey();
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
+ }
+
+ CPubKey pubKey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubKey));
+
+ CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
+
+ // Consistency check.
+ if (!isScript && !(pubKeyAddress.Get() == address.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+
+ // Consistency check.
+ if (isScript) {
+ CBitcoinAddress scriptAddress;
+ CTxDestination destination;
+
+ if (ExtractDestination(script, destination)) {
+ scriptAddress = CBitcoinAddress(destination);
+ if (!(scriptAddress.Get() == pubKeyAddress.Get())) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
+ }
+ }
+ }
+
+ CKeyID vchAddress = pubKey.GetID();
+ pwalletMain->MarkDirty();
+ pwalletMain->SetAddressBook(vchAddress, label, "receive");
+
+ if (pwalletMain->HaveKey(vchAddress)) {
+ return false;
+ }
+
+ pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+
+ if (!pwalletMain->AddKeyPubKey(key, pubKey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
+ }
+
+ if (timestamp < pwalletMain->nTimeFirstKey) {
+ pwalletMain->nTimeFirstKey = timestamp;
+ }
+
+ success = true;
+ }
+
+ // Import scriptPubKey only.
+ if (pubKeys.size() == 0 && keys.size() == 0) {
+ if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
+
+ pwalletMain->MarkDirty();
+
+ if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+
+ if (scriptPubKey.getType() == UniValue::VOBJ) {
+ // add to address book or update label
+ if (address.IsValid()) {
+ pwalletMain->SetAddressBook(address.Get(), label, "receive");
+ }
+ }
+
+ success = true;
+ }
+ }
+
+ UniValue result = UniValue(UniValue::VOBJ);
+ result.pushKV("success", UniValue(success));
+ return result;
+ } catch (const UniValue& e) {
+ UniValue result = UniValue(UniValue::VOBJ);
+ result.pushKV("success", UniValue(false));
+ result.pushKV("error", e);
+ return result;
+ } catch (...) {
+ UniValue result = UniValue(UniValue::VOBJ);
+ result.pushKV("success", UniValue(false));
+ result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
+ return result;
+ }
+}
+
+UniValue importmulti(const JSONRPCRequest& mainRequest)
+{
+ // clang-format off
+ if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
+ throw runtime_error(
+ "importmulti '[<json import requests>]' '<json options>' \n\n"
+ "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n\n"
+ "Arguments:\n"
+ "1. request array (array, required) Data to be imported\n"
+ " [ (array of json objects)\n"
+ " {\n"
+ " \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
+ " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
+ " \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
+ " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
+ " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be be treated as not incoming payments\n"
+ " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
+ " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
+ " \"timestamp\": 1454686740, (integer, optional, default now) Timestamp\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "2. json options (json, optional)\n"
+ " {\n"
+ " \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
+ " }\n"
+ "\nExamples:\n" +
+ HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
+ "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
+ HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
+
+ "\nResponse is an array with the same size as the input that has the execution result :\n"
+ " [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
+
+ // clang-format on
+ if (!EnsureWalletIsAvailable(mainRequest.fHelp)) {
+ return NullUniValue;
+ }
+
+ RPCTypeCheck(mainRequest.params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ));
+
+ const UniValue& requests = mainRequest.params[0];
+
+ //Default options
+ bool fRescan = true;
+
+ if (mainRequest.params.size() > 1) {
+ const UniValue& options = mainRequest.params[1];
+
+ if (options.exists("rescan")) {
+ fRescan = options["rescan"].get_bool();
+ }
+ }
+
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+ EnsureWalletIsUnlocked();
+
+ bool fRunScan = false;
+ const int64_t minimumTimestamp = 1;
+ int64_t nLowestTimestamp;
+
+ if (fRescan && chainActive.Tip()) {
+ nLowestTimestamp = chainActive.Tip()->GetBlockTime();
+ } else {
+ fRescan = false;
+ }
+
+ UniValue response(UniValue::VARR);
+
+ BOOST_FOREACH (const UniValue& data, requests.getValues()) {
+ const UniValue result = processImport(data);
+ response.push_back(result);
+
+ if (!fRescan) {
+ continue;
+ }
+
+ // If at least one request was successful then allow rescan.
+ if (result["success"].get_bool()) {
+ fRunScan = true;
+ }
+
+ // Get the lowest timestamp.
+ const int64_t& timestamp = data.exists("timestamp") && data["timestamp"].get_int64() > minimumTimestamp ? data["timestamp"].get_int64() : minimumTimestamp;
+
+ if (timestamp < nLowestTimestamp) {
+ nLowestTimestamp = timestamp;
+ }
+ }
+
+ if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTime()) {
+ CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindLatestBefore(nLowestTimestamp) : chainActive.Genesis();
+
+ if (pindex) {
+ pwalletMain->ScanForWalletTransactions(pindex, true);
+ pwalletMain->ReacceptWalletTransactions();
+ }
+ }
+
+ return response;
+}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 33620aa6ff..5a22e0278d 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -6,6 +6,7 @@
#include "amount.h"
#include "base58.h"
#include "chain.h"
+#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
#include "main.h"
@@ -362,11 +363,14 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
vecSend.push_back(recipient);
if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance())
- strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
+ strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
+ throw JSONRPCError(RPC_WALLET_ERROR, strError);
+ }
+ CValidationState state;
+ if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) {
+ strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason());
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
- if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get()))
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of the wallet and coins were spent in the copy but not marked as spent here.");
}
UniValue sendtoaddress(const JSONRPCRequest& request)
@@ -959,8 +963,11 @@ UniValue sendmany(const JSONRPCRequest& request)
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
- if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get()))
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
+ CValidationState state;
+ if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), state)) {
+ strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason());
+ throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
+ }
return wtx.GetHash().GetHex();
}
@@ -2279,7 +2286,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix 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 " + CURRENCY_UNIT + "/kB\n"
@@ -2583,6 +2590,7 @@ extern UniValue dumpwallet(const JSONRPCRequest& request);
extern UniValue importwallet(const JSONRPCRequest& request);
extern UniValue importprunedfunds(const JSONRPCRequest& request);
extern UniValue removeprunedfunds(const JSONRPCRequest& request);
+extern UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
@@ -2607,6 +2615,7 @@ static const CRPCCommand commands[] =
{ "wallet", "gettransaction", &gettransaction, false },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false },
{ "wallet", "getwalletinfo", &getwalletinfo, false },
+ { "wallet", "importmulti", &importmulti, true },
{ "wallet", "importprivkey", &importprivkey, true },
{ "wallet", "importwallet", &importwallet, true },
{ "wallet", "importaddress", &importaddress, true },
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 282917d64f..c9e926266c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -8,7 +8,7 @@
#include "base58.h"
#include "checkpoints.h"
#include "chain.h"
-#include "coincontrol.h"
+#include "wallet/coincontrol.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "key.h"
@@ -1463,7 +1463,8 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = *(item.second);
LOCK(mempool.cs);
- wtx.AcceptToMemoryPool(maxTxFee);
+ CValidationState state;
+ wtx.AcceptToMemoryPool(maxTxFee, state);
}
}
@@ -2433,17 +2434,22 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
dPriority = wtxNew.ComputePriority(dPriority, nBytes);
+ // Allow to override the default confirmation target over the CoinControl instance
+ int currentConfirmationTarget = nTxConfirmTarget;
+ if (coinControl && coinControl->nConfirmTarget > 0)
+ currentConfirmationTarget = coinControl->nConfirmTarget;
+
// Can we complete this as a free transaction?
if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
{
// Not enough fee: enough priority?
- double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget);
+ double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget);
// Require at least hard-coded AllowFree.
if (dPriority >= dPriorityNeeded && AllowFree(dPriority))
break;
}
- CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+ CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool);
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
@@ -2474,7 +2480,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
/**
* Call after CreateTransaction unless you want to abort
*/
-bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman)
+bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state)
{
{
LOCK2(cs_main, cs_wallet);
@@ -2502,9 +2508,9 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
if (fBroadcastTransactions)
{
// Broadcast
- if (!wtxNew.AcceptToMemoryPool(maxTxFee)) {
+ if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) {
// This must not fail. The transaction has already been signed and recorded.
- LogPrintf("CommitTransaction(): Error: Transaction not valid\n");
+ LogPrintf("CommitTransaction(): Error: Transaction not valid, %s\n", state.GetRejectReason());
return false;
}
wtxNew.RelayWalletTransaction(connman);
@@ -3466,8 +3472,6 @@ bool CWallet::InitLoadWallet()
LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
}
- // Add wallet transactions that aren't already in a block to mapTransactions
- walletInstance->ReacceptWalletTransactions();
pwalletMain = walletInstance;
@@ -3651,8 +3655,7 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee)
+bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- CValidationState state;
return ::AcceptToMemoryPool(mempool, state, *this, true, NULL, false, nAbsurdFee);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3b37f7cb1f..c33a6ca91f 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -215,7 +215,7 @@ public:
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
int GetBlocksToMaturity() const;
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(const CAmount& nAbsurdFee);
+ bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
@@ -774,7 +774,7 @@ public:
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
- bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman);
+ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
bool AddAccountingEntry(const CAccountingEntry&);