diff options
Diffstat (limited to 'src')
89 files changed, 2213 insertions, 942 deletions
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 8c024a8c4a..c83432e91a 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -14,6 +14,9 @@ bench_bench_bitcoin_SOURCES = \ bench/Examples.cpp \ bench/rollingbloom.cpp \ bench/crypto_hash.cpp \ + bench/ccoins_caching.cpp \ + bench/mempool_eviction.cpp \ + bench/verify_script.cpp \ bench/base58.cpp bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ @@ -34,7 +37,8 @@ bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif if ENABLE_WALLET -bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) +bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp +bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO) endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) diff --git a/src/addrman.cpp b/src/addrman.cpp index bfb8e94575..2016523212 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -358,8 +358,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT); int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE); while (vvTried[nKBucket][nKBucketPos] == -1) { - nKBucket = (nKBucket + insecure_rand()) % ADDRMAN_TRIED_BUCKET_COUNT; - nKBucketPos = (nKBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE; + nKBucket = (nKBucket + insecure_rand.rand32()) % ADDRMAN_TRIED_BUCKET_COUNT; + nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE; } int nId = vvTried[nKBucket][nKBucketPos]; assert(mapInfo.count(nId) == 1); @@ -375,8 +375,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT); int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE); while (vvNew[nUBucket][nUBucketPos] == -1) { - nUBucket = (nUBucket + insecure_rand()) % ADDRMAN_NEW_BUCKET_COUNT; - nUBucketPos = (nUBucketPos + insecure_rand()) % ADDRMAN_BUCKET_SIZE; + nUBucket = (nUBucket + insecure_rand.rand32()) % ADDRMAN_NEW_BUCKET_COUNT; + nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE; } int nId = vvNew[nUBucket][nUBucketPos]; assert(mapInfo.count(nId) == 1); diff --git a/src/addrman.h b/src/addrman.h index 9bab39049d..e9e137c978 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -211,6 +211,9 @@ protected: //! secret key to randomize bucket select with uint256 nKey; + //! Source of random numbers for randomization in inner loops + FastRandomContext insecure_rand; + //! Find an entry. CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL); diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp new file mode 100644 index 0000000000..1e8e3d462f --- /dev/null +++ b/src/bench/ccoins_caching.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 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. + +#include "bench.h" +#include "coins.h" +#include "policy/policy.h" +#include "wallet/crypter.h" + +#include <vector> + +// FIXME: Dedup with SetupDummyInputs in test/transaction_tests.cpp. +// +// Helper: create two dummy transactions, each with +// two outputs. The first has 11 and 50 CENT outputs +// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs +// paid to a TX_PUBKEYHASH. +// +static std::vector<CMutableTransaction> +SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) +{ + std::vector<CMutableTransaction> dummyTransactions; + dummyTransactions.resize(2); + + // Add some keys to the keystore: + CKey key[4]; + for (int i = 0; i < 4; i++) { + key[i].MakeNewKey(i % 2); + keystoreRet.AddKey(key[i]); + } + + // Create some dummy input transactions + dummyTransactions[0].vout.resize(2); + dummyTransactions[0].vout[0].nValue = 11 * CENT; + dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; + dummyTransactions[0].vout[1].nValue = 50 * CENT; + dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG; + coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0); + + dummyTransactions[1].vout.resize(2); + dummyTransactions[1].vout[0].nValue = 21 * CENT; + dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID()); + dummyTransactions[1].vout[1].nValue = 22 * CENT; + dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID()); + coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0); + + return dummyTransactions; +} + +// Microbenchmark for simple accesses to a CCoinsViewCache database. Note from +// laanwj, "replicating the actual usage patterns of the client is hard though, +// many times micro-benchmarks of the database showed completely different +// characteristics than e.g. reindex timings. But that's not a requirement of +// every benchmark." +// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) +static void CCoinsCaching(benchmark::State& state) +{ + CBasicKeyStore keystore; + CCoinsView coinsDummy; + CCoinsViewCache coins(&coinsDummy); + std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); + + CMutableTransaction t1; + t1.vin.resize(3); + t1.vin[0].prevout.hash = dummyTransactions[0].GetHash(); + t1.vin[0].prevout.n = 1; + t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0); + t1.vin[1].prevout.hash = dummyTransactions[1].GetHash(); + t1.vin[1].prevout.n = 0; + t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4); + t1.vin[2].prevout.hash = dummyTransactions[1].GetHash(); + t1.vin[2].prevout.n = 1; + t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4); + t1.vout.resize(2); + t1.vout[0].nValue = 90 * CENT; + t1.vout[0].scriptPubKey << OP_1; + + // Benchmark. + while (state.KeepRunning()) { + bool success = AreInputsStandard(t1, coins); + assert(success); + CAmount value = coins.GetValueIn(t1); + assert(value == (50 + 21 + 22) * CENT); + } +} + +BENCHMARK(CCoinsCaching); diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp new file mode 100644 index 0000000000..7091ee3e11 --- /dev/null +++ b/src/bench/coin_selection.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bench.h" +#include "wallet/wallet.h" + +#include <boost/foreach.hpp> +#include <set> + +using namespace std; + +static void addCoin(const CAmount& nValue, const CWallet& wallet, vector<COutput>& vCoins) +{ + int nInput = 0; + + static int nextLockTime = 0; + CMutableTransaction tx; + tx.nLockTime = nextLockTime++; // so all transactions get different hashes + tx.vout.resize(nInput + 1); + tx.vout[nInput].nValue = nValue; + CWalletTx* wtx = new CWalletTx(&wallet, tx); + + int nAge = 6 * 24; + COutput output(wtx, nInput, nAge, true, true); + vCoins.push_back(output); +} + +// Simple benchmark for wallet coin selection. Note that it maybe be necessary +// to build up more complicated scenarios in order to get meaningful +// measurements of performance. From laanwj, "Wallet coin selection is probably +// the hardest, as you need a wider selection of scenarios, just testing the +// same one over and over isn't too useful. Generating random isn't useful +// either for measurements." +// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) +static void CoinSelection(benchmark::State& state) +{ + const CWallet wallet; + vector<COutput> vCoins; + LOCK(wallet.cs_wallet); + + while (state.KeepRunning()) { + // Empty wallet. + BOOST_FOREACH (COutput output, vCoins) + delete output.tx; + vCoins.clear(); + + // Add coins. + for (int i = 0; i < 1000; i++) + addCoin(1000 * COIN, wallet, vCoins); + addCoin(3 * COIN, wallet, vCoins); + + set<pair<const CWalletTx*, unsigned int> > setCoinsRet; + CAmount nValueRet; + bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet); + assert(success); + assert(nValueRet == 1003 * COIN); + assert(setCoinsRet.size() == 2); + } +} + +BENCHMARK(CoinSelection); diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp new file mode 100644 index 0000000000..0ae69c75fc --- /dev/null +++ b/src/bench/mempool_eviction.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2011-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bench.h" +#include "policy/policy.h" +#include "txmempool.h" + +#include <list> +#include <vector> + +static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool) +{ + int64_t nTime = 0; + double dPriority = 10.0; + unsigned int nHeight = 1; + bool spendsCoinbase = false; + unsigned int sigOpCost = 4; + LockPoints lp; + pool.addUnchecked(tx.GetHash(), CTxMemPoolEntry( + tx, nFee, nTime, dPriority, nHeight, pool.HasNoInputsOf(tx), + tx.GetValueOut(), spendsCoinbase, sigOpCost, lp)); +} + +// Right now this is only testing eviction performance in an extremely small +// mempool. Code needs to be written to generate a much wider variety of +// unique transactions for a more meaningful performance measurement. +static void MempoolEviction(benchmark::State& state) +{ + CMutableTransaction tx1 = CMutableTransaction(); + tx1.vin.resize(1); + tx1.vin[0].scriptSig = CScript() << OP_1; + tx1.vout.resize(1); + tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; + tx1.vout[0].nValue = 10 * COIN; + + CMutableTransaction tx2 = CMutableTransaction(); + tx2.vin.resize(1); + tx2.vin[0].scriptSig = CScript() << OP_2; + tx2.vout.resize(1); + tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; + tx2.vout[0].nValue = 10 * COIN; + + CMutableTransaction tx3 = CMutableTransaction(); + tx3.vin.resize(1); + tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); + tx3.vin[0].scriptSig = CScript() << OP_2; + tx3.vout.resize(1); + tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; + tx3.vout[0].nValue = 10 * COIN; + + CMutableTransaction tx4 = CMutableTransaction(); + tx4.vin.resize(2); + tx4.vin[0].prevout.SetNull(); + tx4.vin[0].scriptSig = CScript() << OP_4; + tx4.vin[1].prevout.SetNull(); + tx4.vin[1].scriptSig = CScript() << OP_4; + tx4.vout.resize(2); + tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; + tx4.vout[0].nValue = 10 * COIN; + tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL; + tx4.vout[1].nValue = 10 * COIN; + + CMutableTransaction tx5 = CMutableTransaction(); + tx5.vin.resize(2); + tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0); + tx5.vin[0].scriptSig = CScript() << OP_4; + tx5.vin[1].prevout.SetNull(); + tx5.vin[1].scriptSig = CScript() << OP_5; + tx5.vout.resize(2); + tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; + tx5.vout[0].nValue = 10 * COIN; + tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL; + tx5.vout[1].nValue = 10 * COIN; + + CMutableTransaction tx6 = CMutableTransaction(); + tx6.vin.resize(2); + tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1); + tx6.vin[0].scriptSig = CScript() << OP_4; + tx6.vin[1].prevout.SetNull(); + tx6.vin[1].scriptSig = CScript() << OP_6; + tx6.vout.resize(2); + tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; + tx6.vout[0].nValue = 10 * COIN; + tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL; + tx6.vout[1].nValue = 10 * COIN; + + CMutableTransaction tx7 = CMutableTransaction(); + tx7.vin.resize(2); + tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0); + tx7.vin[0].scriptSig = CScript() << OP_5; + tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0); + tx7.vin[1].scriptSig = CScript() << OP_6; + tx7.vout.resize(2); + tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; + tx7.vout[0].nValue = 10 * COIN; + tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; + tx7.vout[1].nValue = 10 * COIN; + + CTxMemPool pool(CFeeRate(1000)); + + while (state.KeepRunning()) { + AddTx(tx1, 10000LL, pool); + AddTx(tx2, 5000LL, pool); + AddTx(tx3, 20000LL, pool); + AddTx(tx4, 7000LL, pool); + AddTx(tx5, 1000LL, pool); + AddTx(tx6, 1100LL, pool); + AddTx(tx7, 9000LL, pool); + pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); + pool.TrimToSize(GetVirtualTransactionSize(tx1)); + } +} + +BENCHMARK(MempoolEviction); diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp new file mode 100644 index 0000000000..dc3940cdbd --- /dev/null +++ b/src/bench/verify_script.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 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. + +#include "bench.h" +#include "key.h" +#if defined(HAVE_CONSENSUS_LIB) +#include "script/bitcoinconsensus.h" +#endif +#include "script/script.h" +#include "script/sign.h" +#include "streams.h" + +// FIXME: Dedup with BuildCreditingTransaction in test/script_tests.cpp. +static CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey) +{ + CMutableTransaction txCredit; + txCredit.nVersion = 1; + txCredit.nLockTime = 0; + txCredit.vin.resize(1); + txCredit.vout.resize(1); + txCredit.vin[0].prevout.SetNull(); + txCredit.vin[0].scriptSig = CScript() << CScriptNum(0) << CScriptNum(0); + txCredit.vin[0].nSequence = CTxIn::SEQUENCE_FINAL; + txCredit.vout[0].scriptPubKey = scriptPubKey; + txCredit.vout[0].nValue = 1; + + return txCredit; +} + +// FIXME: Dedup with BuildSpendingTransaction in test/script_tests.cpp. +static CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMutableTransaction& txCredit) +{ + CMutableTransaction txSpend; + txSpend.nVersion = 1; + txSpend.nLockTime = 0; + txSpend.vin.resize(1); + txSpend.vout.resize(1); + txSpend.wit.vtxinwit.resize(1); + txSpend.vin[0].prevout.hash = txCredit.GetHash(); + txSpend.vin[0].prevout.n = 0; + txSpend.vin[0].scriptSig = scriptSig; + txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL; + txSpend.vout[0].scriptPubKey = CScript(); + txSpend.vout[0].nValue = txCredit.vout[0].nValue; + + return txSpend; +} + +// Microbenchmark for verification of a basic P2WPKH script. Can be easily +// modified to measure performance of other types of scripts. +static void VerifyScriptBench(benchmark::State& state) +{ + const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH; + const int witnessversion = 0; + + // Keypair. + CKey key; + const unsigned char vchKey[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + key.Set(vchKey, vchKey + 32, false); + CPubKey pubkey = key.GetPubKey(); + uint160 pubkeyHash; + CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin()); + + // Script. + CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash); + CScript scriptSig; + CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; + CTransaction txCredit = BuildCreditingTransaction(scriptPubKey); + CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); + CScriptWitness& witness = txSpend.wit.vtxinwit[0].scriptWitness; + witness.stack.emplace_back(); + key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SIGVERSION_WITNESS_V0), witness.stack.back(), 0); + witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL)); + witness.stack.push_back(ToByteVector(pubkey)); + + // Benchmark. + while (state.KeepRunning()) { + ScriptError err; + bool success = VerifyScript( + txSpend.vin[0].scriptSig, + txCredit.vout[0].scriptPubKey, + &txSpend.wit.vtxinwit[0].scriptWitness, + flags, + MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue), + &err); + assert(err == SCRIPT_ERR_OK); + assert(success); + +#if defined(HAVE_CONSENSUS_LIB) + CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); + stream << txSpend; + int csuccess = bitcoinconsensus_verify_script_with_amount( + begin_ptr(txCredit.vout[0].scriptPubKey), + txCredit.vout[0].scriptPubKey.size(), + txCredit.vout[0].nValue, + (const unsigned char*)&stream[0], stream.size(), 0, flags, nullptr); + assert(csuccess == 1); +#endif + } +} + +BENCHMARK(VerifyScriptBench); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 9d4c4e53bd..2d66448d80 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -223,7 +223,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params) evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); // Attach request data - std::string strRequest = JSONRPCRequest(strMethod, params, 1); + std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req); assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index cb863bda19..e09afd632e 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -164,7 +164,7 @@ static void RegisterLoad(const string& strInput) static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal) { int64_t newVersion = atoi64(cmdVal); - if (newVersion < 1 || newVersion > CTransaction::CURRENT_VERSION) + if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) throw runtime_error("Invalid TX version requested"); tx.nVersion = (int) newVersion; diff --git a/src/chain.h b/src/chain.h index 6588e8f57d..e2f8c56522 100644 --- a/src/chain.h +++ b/src/chain.h @@ -200,7 +200,7 @@ public: unsigned int nNonce; //! (memory only) Sequential id assigned to distinguish order in which blocks are received. - uint32_t nSequenceId; + int32_t nSequenceId; void SetNull() { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e6be1b5d5b..5850016ae2 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -91,10 +91,10 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1462060800; // May 1st, 2016 consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017 - // Deployment of SegWit (BIP141 and BIP143) + // Deployment of SegWit (BIP141, BIP143, and BIP147) consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 0; // Never / undefined + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1479168000; // November 15th, 2016. + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1510704000; // November 15th, 2017. /** * The message start string is designed to be unlikely to occur in normal data. @@ -113,12 +113,13 @@ public: assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be", true)); // Pieter Wuille - vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me")); // Matt Corallo + // Note that of those with the service bits flag, most only support a subset of possible options + vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be", true)); // Pieter Wuille, only supports x1, x5, x9, and xd + vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me", true)); // Matt Corallo, only supports x9 vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); // Luke Dashjr - vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); // Christian Decker + vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com", true)); // Christian Decker, supports x1 - xf vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); // Jeff Garzik - vSeeds.push_back(CDNSSeedData("bitcoin.jonasschnelli.ch", "seed.bitcoin.jonasschnelli.ch", true)); // Jonas Schnelli + vSeeds.push_back(CDNSSeedData("bitcoin.jonasschnelli.ch", "seed.bitcoin.jonasschnelli.ch", true)); // Jonas Schnelli, only supports x1, x5, x9, and xd base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); @@ -132,7 +133,6 @@ public: fDefaultConsistencyChecks = false; fRequireStandard = true; fMineBlocksOnDemand = false; - fTestnetToBeDeprecatedFieldRPC = false; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -186,7 +186,7 @@ public: consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1456790400; // March 1st, 2016 consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017 - // Deployment of SegWit (BIP141 and BIP143) + // Deployment of SegWit (BIP141, BIP143, and BIP147) consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 1462060800; // May 1st 2016 consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1493596800; // May 1st 2017 @@ -223,7 +223,6 @@ public: fDefaultConsistencyChecks = false; fRequireStandard = false; fMineBlocksOnDemand = false; - fTestnetToBeDeprecatedFieldRPC = true; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -285,7 +284,6 @@ public: fDefaultConsistencyChecks = true; fRequireStandard = false; fMineBlocksOnDemand = true; - fTestnetToBeDeprecatedFieldRPC = false; checkpointData = (CCheckpointData){ boost::assign::map_list_of diff --git a/src/chainparams.h b/src/chainparams.h index 0c3820b7c6..633fbd5120 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -67,8 +67,6 @@ public: uint64_t PruneAfterHeight() const { return nPruneAfterHeight; } /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } - /** In the future use NetworkIDString() for RPC fields */ - bool TestnetToBeDeprecatedFieldRPC() const { return fTestnetToBeDeprecatedFieldRPC; } /** Return the BIP70 network string (main, test or regtest) */ std::string NetworkIDString() const { return strNetworkID; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } @@ -91,7 +89,6 @@ protected: bool fDefaultConsistencyChecks; bool fRequireStandard; bool fMineBlocksOnDemand; - bool fTestnetToBeDeprecatedFieldRPC; CCheckpointData checkpointData; }; diff --git a/src/consensus/params.h b/src/consensus/params.h index 5b2f49184f..0e73cace83 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -16,7 +16,7 @@ enum DeploymentPos { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. - DEPLOYMENT_SEGWIT, // Deployment of BIP141 and BIP143 + DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147. // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 6a6c5276cc..e35acb6cd9 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -127,7 +127,7 @@ static bool multiUserAuthorized(std::string strUserPass) return false; } -static bool RPCAuthorized(const std::string& strAuth) +static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut) { if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called return false; @@ -136,7 +136,10 @@ static bool RPCAuthorized(const std::string& strAuth) std::string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); std::string strUserPass = DecodeBase64(strUserPass64); - + + if (strUserPass.find(":") != std::string::npos) + strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":")); + //Check if authorized under single-user field if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) { return true; @@ -159,7 +162,8 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) return false; } - if (!RPCAuthorized(authHeader.second)) { + JSONRPCRequest jreq; + if (!RPCAuthorized(authHeader.second, jreq.authUser)) { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString()); /* Deter brute-forcing @@ -172,19 +176,21 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) return false; } - JSONRequest jreq; try { // Parse request UniValue valRequest; if (!valRequest.read(req->ReadBody())) throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + // Set the URI + jreq.URI = req->GetURI(); + std::string strReply; // singleton request if (valRequest.isObject()) { jreq.parse(valRequest); - UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + UniValue result = tableRPC.execute(jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); diff --git a/src/init.cpp b/src/init.cpp index eefef7ba0b..2b1fbed072 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -72,6 +72,7 @@ static const bool DEFAULT_DISABLE_SAFEMODE = false; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr<CConnman> g_connman; +std::unique_ptr<PeerLogicValidation> peerLogic; #if ENABLE_ZMQ static CZMQNotificationInterface* pzmqNotificationInterface = NULL; @@ -200,6 +201,8 @@ void Shutdown() pwalletMain->Flush(false); #endif MapPort(false); + UnregisterValidationInterface(peerLogic.get()); + peerLogic.reset(); g_connman.reset(); StopTorControl(); @@ -423,7 +426,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)"); } - string debugCategories = "addrman, alert, bench, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below + string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + @@ -1102,6 +1105,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()))); CConnman& connman = *g_connman; + peerLogic.reset(new PeerLogicValidation(&connman)); + RegisterValidationInterface(peerLogic.get()); RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size diff --git a/src/main.cpp b/src/main.cpp index f3b0e3c22e..50158b4687 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -169,7 +169,11 @@ namespace { */ CCriticalSection cs_nBlockSequenceId; /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ - uint32_t nBlockSequenceId = 1; + int32_t nBlockSequenceId = 1; + /** Decreasing counter (used by subsequent preciousblock calls). */ + int32_t nBlockReverseSequenceId = -1; + /** chainwork for the last block that preciousblock has been applied to. */ + arith_uint256 nLastPreciousChainwork = 0; /** * Sources of received blocks, saved to be able to send them reject @@ -485,9 +489,13 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pf return; } if (nodestate->fProvidesHeaderAndIDs) { - BOOST_FOREACH(const NodeId nodeid, lNodesAnnouncingHeaderAndIDs) - if (nodeid == pfrom->GetId()) + for (std::list<NodeId>::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) { + if (*it == pfrom->GetId()) { + lNodesAnnouncingHeaderAndIDs.erase(it); + lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return; + } + } bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { @@ -1273,6 +1281,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (fRequireStandard && !AreInputsStandard(tx, view)) return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); + // Check for non-standard witness in P2WSH + if (!tx.wit.IsNull() && fRequireStandard && !IsWitnessStandard(tx, view)) + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true); + int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); CAmount nValueOut = tx.GetValueOut(); @@ -1555,7 +1567,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - SyncWithWallets(tx, NULL); + GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); return true; } @@ -1846,10 +1858,10 @@ void Misbehaving(NodeId pnode, int howmuch) int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { - LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrintf("%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior); state->fShouldBan = true; } else - LogPrintf("%s: %s (%d -> %d)\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrintf("%s: %s peer=%d (%d -> %d)\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior); } void static InvalidChainFound(CBlockIndex* pindexNew) @@ -1870,17 +1882,6 @@ void static InvalidChainFound(CBlockIndex* pindexNew) } void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { - int nDoS = 0; - if (state.IsInvalid(nDoS)) { - std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash()); - if (it != mapBlockSource.end() && State(it->second)) { - assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes - CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; - State(it->second)->rejects.push_back(reject); - if (nDoS > 0) - Misbehaving(it->second, nDoS); - } - } if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -2788,7 +2789,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx, pindexDelete->pprev); + GetMainSignals().SyncTransaction(tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); } return true; } @@ -2827,7 +2828,6 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, InvalidBlockFound(pindexNew, state); return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } - mapBlockSource.erase(pindexNew->GetBlockHash()); nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); assert(view.Flush()); @@ -3026,7 +3026,7 @@ static void NotifyHeaderTip() { * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock, CConnman* connman) { +bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexNewTip = NULL; std::vector<std::tuple<CTransaction,CBlockIndex*,int>> txChanged; @@ -3041,7 +3041,6 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlockIndex *pindexFork; std::list<CTransaction> txConflicted; bool fInitialDownload; - int nNewHeight; { LOCK(cs_main); CBlockIndex *pindexOldTip = chainActive.Tip(); @@ -3064,59 +3063,27 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(); - nNewHeight = chainActive.Height(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). // Notifications/callbacks that can run without cs_main - if(connman) - connman->SetBestHeight(nNewHeight); // throw all transactions though the signal-interface // while _not_ holding the cs_main lock BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx, pindexNewTip); + 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++) - SyncWithWallets(std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i])); + GetMainSignals().SyncTransaction(std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i])); + + // Notify external listeners about the new tip. + GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); // Always notify the UI if a new block tip was connected if (pindexFork != pindexNewTip) { uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); - - if (!fInitialDownload) { - // Find the hashes of all blocks that weren't previously in the best chain. - std::vector<uint256> vHashes; - CBlockIndex *pindexToAnnounce = pindexNewTip; - while (pindexToAnnounce != pindexFork) { - vHashes.push_back(pindexToAnnounce->GetBlockHash()); - pindexToAnnounce = pindexToAnnounce->pprev; - if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { - // Limit announcements in case of a huge reorganization. - // Rely on the peer's synchronization mechanism in that case. - break; - } - } - // Relay inventory, but don't relay old inventory during initial block download. - int nBlockEstimate = 0; - if (fCheckpointsEnabled) - nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); - if(connman) { - connman->ForEachNode([nNewHeight, nBlockEstimate, &vHashes](CNode* pnode) { - if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { - BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) { - pnode->PushBlockHash(hash); - } - } - }); - } - // Notify external listeners about the new tip. - if (!vHashes.empty()) { - GetMainSignals().UpdatedBlockTip(pindexNewTip); - } - } } } while (pindexNewTip != pindexMostWork); CheckBlockIndex(chainparams.GetConsensus()); @@ -3129,6 +3096,36 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, return true; } + +bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) +{ + { + LOCK(cs_main); + if (pindex->nChainWork < chainActive.Tip()->nChainWork) { + // Nothing to do, this block is not at the tip. + return true; + } + if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) { + // The chain has been extended since the last call, reset the counter. + nBlockReverseSequenceId = -1; + } + nLastPreciousChainwork = chainActive.Tip()->nChainWork; + setBlockIndexCandidates.erase(pindex); + pindex->nSequenceId = nBlockReverseSequenceId; + if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) { + // We can't keep reducing the counter if somebody really wants to + // call preciousblock 2**31-1 times on the same set of tips... + nBlockReverseSequenceId--; + } + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) { + setBlockIndexCandidates.insert(pindex); + PruneBlockIndexCandidates(); + } + } + + return ActivateBestChain(state, params); +} + bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { AssertLockHeld(cs_main); @@ -3745,7 +3742,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha return true; } -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman) +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) { { LOCK(cs_main); @@ -3767,7 +3764,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C NotifyHeaderTip(); - if (!ActivateBestChain(state, chainparams, pblock, connman)) + if (!ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed", __func__); return true; @@ -4523,7 +4520,7 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. } - if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked + if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock) // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred). // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. if (!fHavePruned) { @@ -4702,6 +4699,59 @@ std::string GetWarnings(const std::string& strFor) ////////////////////////////////////////////////////////////////////////////// // +// blockchain -> download logic notification +// + +void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { + const int nNewHeight = pindexNew->nHeight; + connman->SetBestHeight(nNewHeight); + + if (!fInitialDownload) { + // Find the hashes of all blocks that weren't previously in the best chain. + std::vector<uint256> vHashes; + const CBlockIndex *pindexToAnnounce = pindexNew; + while (pindexToAnnounce != pindexFork) { + vHashes.push_back(pindexToAnnounce->GetBlockHash()); + pindexToAnnounce = pindexToAnnounce->pprev; + if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { + // Limit announcements in case of a huge reorganization. + // Rely on the peer's synchronization mechanism in that case. + break; + } + } + // Relay inventory, but don't relay old inventory during initial block download. + connman->ForEachNode([nNewHeight, &vHashes](CNode* pnode) { + if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) { + BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) { + pnode->PushBlockHash(hash); + } + } + }); + } +} + +void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) { + LOCK(cs_main); + + const uint256 hash(block.GetHash()); + std::map<uint256, NodeId>::iterator it = mapBlockSource.find(hash); + + int nDoS = 0; + if (state.IsInvalid(nDoS)) { + if (it != mapBlockSource.end() && State(it->second)) { + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes + CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash}; + State(it->second)->rejects.push_back(reject); + if (nDoS > 0) + Misbehaving(it->second, nDoS); + } + } + if (it != mapBlockSource.end()) + mapBlockSource.erase(it); +} + +////////////////////////////////////////////////////////////////////////////// +// // Messages // @@ -4758,6 +4808,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma uint64_t hashAddr = addr.GetHash(); std::multimap<uint64_t, CNode*> mapMix; const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); + FastRandomContext insecure_rand; auto sortfunc = [&mapMix, &hasher](CNode* pnode) { if (pnode->nVersion >= CADDR_TIME_VERSION) { @@ -4766,9 +4817,9 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma } }; - auto pushfunc = [&addr, &mapMix, &nRelayNodes] { + auto pushfunc = [&addr, &mapMix, &nRelayNodes, &insecure_rand] { for (auto mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) - mi->second->PushAddress(addr); + mi->second->PushAddress(addr, insecure_rand); }; connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); @@ -4870,7 +4921,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // and we don't feel like constructing the object for them, so // instead we respond with the full, non-compact block. bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness; - if (mi->second->nHeight >= chainActive.Height() - 10) { + if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness); pfrom->PushMessageWithFlag(fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); } else @@ -5078,14 +5129,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (fListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices()); + FastRandomContext insecure_rand; if (addr.IsRoutable()) { LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString()); - pfrom->PushAddress(addr); + pfrom->PushAddress(addr, insecure_rand); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(pfrom->addrLocal); LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString()); - pfrom->PushAddress(addr); + pfrom->PushAddress(addr, insecure_rand); } } @@ -5397,8 +5449,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - if (it->second->nHeight < chainActive.Height() - 15) { - LogPrint("net", "Peer %d sent us a getblocktxn for a block > 15 deep", pfrom->id); + if (it->second->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) { + LogPrint("net", "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->id, MAX_BLOCKTXN_DEPTH); return true; } @@ -5727,6 +5779,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } + if (!fAlreadyInFlight && mapBlocksInFlight.size() == 1 && pindex->pprev->IsValid(BLOCK_VALID_CHAIN)) { + // We seem to be rather well-synced, so it appears pfrom was the first to provide us + // with this block! Let's get them to announce using compact blocks in the future. + MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman); + } + BlockTransactionsRequest req; for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { if (!partialBlock.IsTxAvailable(i)) @@ -5795,7 +5853,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage(NetMsgType::GETDATA, invs); } else { CValidationState state; - ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL, &connman); + ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL); int nDoS; if (state.IsInvalid(nDoS)) { assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes @@ -5971,7 +6029,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Such an unrequested block may still be processed, subject to the // conditions in AcceptBlock(). bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); - ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, &connman); + ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL); int nDoS; if (state.IsInvalid(nDoS)) { assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes @@ -6008,8 +6066,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->vAddrToSend.clear(); vector<CAddress> vAddr = connman.GetAddresses(); + FastRandomContext insecure_rand; BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); + pfrom->PushAddress(addr, insecure_rand); } @@ -6842,7 +6901,7 @@ bool SendMessages(CNode* pto, CConnman& connman) // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter && (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) { - pto->nextSendTimeFeeFilter = timeNow + (insecure_rand() % MAX_FEEFILTER_CHANGE_DELAY) * 1000000; + pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000; } } } @@ -6859,6 +6918,12 @@ 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); +} + class CMainCleanup { public: diff --git a/src/main.h b/src/main.h index 2646d8f86b..3eab9b89da 100644 --- a/src/main.h +++ b/src/main.h @@ -16,6 +16,7 @@ #include "net.h" #include "script/script_error.h" #include "sync.h" +#include "validationinterface.h" #include "versionbits.h" #include <algorithm> @@ -41,7 +42,6 @@ class CValidationInterface; class CValidationState; struct PrecomputedTransactionData; -struct CNodeStateStats; struct LockPoints; /** Default for DEFAULT_WHITELISTRELAY. */ @@ -90,6 +90,11 @@ static const unsigned int BLOCK_STALLING_TIMEOUT = 2; /** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends * less than this number, we reached its tip. Changing this value is a protocol upgrade. */ static const unsigned int MAX_HEADERS_RESULTS = 2000; +/** Maximum depth of blocks we're willing to serve as compact blocks to peers + * when requested. For older blocks, a regular BLOCK response will be sent. */ +static const int MAX_CMPCTBLOCK_DEPTH = 5; +/** Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */ +static const int MAX_BLOCKTXN_DEPTH = 10; /** Size of the "block download window": how far ahead of our current height do we fetch? * Larger windows tolerate larger download speed differences between peer, but increase the potential * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning @@ -206,11 +211,6 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3; // Setting the target to > than 550MB will make it likely we can respect the target. static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; -/** Register with a network node to receive its signals */ -void RegisterNodeSignals(CNodeSignals& nodeSignals); -/** Unregister a network node */ -void UnregisterNodeSignals(CNodeSignals& nodeSignals); - /** * Process an incoming block. This only returns after the best known valid * block is made active. Note that it does not, however, guarantee that the @@ -223,7 +223,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); * @param[out] dbp The already known disk position of pblock, or NULL if not yet stored. * @return True if state.IsValid() */ -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman); +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -240,15 +240,6 @@ bool InitBlockIndex(const CChainParams& chainparams); bool LoadBlockIndex(); /** Unload database information */ void UnloadBlockIndex(); -/** Process protocol messages received from a given node */ -bool ProcessMessages(CNode* pfrom, CConnman& connman); -/** - * Send queued protocol messages to be sent to a give node. - * - * @param[in] pto The node which we are sending messages to. - * @param[in] connman The connection manager for that node. - */ -bool SendMessages(CNode* pto, CConnman& connman); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ @@ -264,7 +255,7 @@ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ -bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL, CConnman* connman = NULL); +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** @@ -291,10 +282,6 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); -/** Get statistics from node state */ -bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); -/** Increase a node's misbehavior score. */ -void Misbehaving(NodeId nodeid, int howmuch); /** Flush all state, indexes and buffers to disk. */ void FlushStateToDisk(); /** Prune block files and flush state to disk. */ @@ -310,14 +297,8 @@ 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); -struct CNodeStateStats { - int nMisbehavior; - int nSyncHeight; - int nCommonHeight; - std::vector<int> vHeightInFlight; -}; - - +/** 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 @@ -504,6 +485,9 @@ public: /** Find the last common block between the parameter chain and a locator. */ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator); +/** Mark a block as precious and reorganize. */ +bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex); + /** Mark a block as invalid. */ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex); @@ -545,4 +529,45 @@ static const unsigned int REJECT_ALREADY_KNOWN = 0x101; /** Transaction conflicts with a transaction already known */ static const unsigned int REJECT_CONFLICT = 0x102; +// The following things handle network-processing logic +// (and should be moved to a separate file) + +/** Register with a network node to receive its signals */ +void RegisterNodeSignals(CNodeSignals& nodeSignals); +/** Unregister a network node */ +void UnregisterNodeSignals(CNodeSignals& nodeSignals); + +class PeerLogicValidation : public CValidationInterface { +private: + CConnman* connman; + +public: + PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {} + + virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload); + virtual void BlockChecked(const CBlock& block, const CValidationState& state); +}; + +struct CNodeStateStats { + int nMisbehavior; + int nSyncHeight; + int nCommonHeight; + std::vector<int> vHeightInFlight; +}; + +/** Get statistics from node state */ +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); +/** Increase a node's misbehavior score. */ +void Misbehaving(NodeId nodeid, int howmuch); + +/** Process protocol messages received from a given node */ +bool ProcessMessages(CNode* pfrom, CConnman& connman); +/** + * Send queued protocol messages to be sent to a give node. + * + * @param[in] pto The node which we are sending messages to. + * @param[in] connman The connection manager for that node. + */ +bool SendMessages(CNode* pto, CConnman& connman); + #endif // BITCOIN_MAIN_H diff --git a/src/miner.cpp b/src/miner.cpp index 9575858840..ebf2f21ffd 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -29,6 +29,7 @@ #include <boost/thread.hpp> #include <boost/tuple/tuple.hpp> #include <queue> +#include <utility> using namespace std; @@ -122,14 +123,14 @@ void BlockAssembler::resetBlock() blockFinished = false; } -CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) +std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) { resetBlock(); pblocktemplate.reset(new CBlockTemplate()); if(!pblocktemplate.get()) - return NULL; + return nullptr; pblock = &pblocktemplate->block; // pointer for convenience // Add dummy coinbase tx as first transaction @@ -194,7 +195,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); } - return pblocktemplate.release(); + return std::move(pblocktemplate); } bool BlockAssembler::isStillDependent(CTxMemPool::txiter iter) diff --git a/src/miner.h b/src/miner.h index 11753f5e43..bad443b82a 100644 --- a/src/miner.h +++ b/src/miner.h @@ -164,7 +164,7 @@ private: public: BlockAssembler(const CChainParams& chainparams); /** Construct a new block template with coinbase to scriptPubKeyIn */ - CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); + std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn); private: // utility functions diff --git a/src/net.cpp b/src/net.cpp index 19dd040997..1bca168d1d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -187,7 +187,8 @@ void AdvertiseLocal(CNode *pnode) if (addrLocal.IsRoutable()) { LogPrint("net", "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); - pnode->PushAddress(addrLocal); + FastRandomContext insecure_rand; + pnode->PushAddress(addrLocal, insecure_rand); } } } @@ -659,7 +660,7 @@ void CNode::copyStats(CNodeStats &stats) // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) stats.dPingTime = (((double)nPingUsecTime) / 1e6); - stats.dPingMin = (((double)nMinPingUsecTime) / 1e6); + stats.dMinPing = (((double)nMinPingUsecTime) / 1e6); stats.dPingWait = (((double)nPingUsecWait) / 1e6); // Leave string empty if addrLocal invalid (not filled in yet) @@ -1454,6 +1455,7 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe return data.host; } + // See chainparams.cpp, most dnsseeds only support one or two possible servicebits hostnames return strprintf("x%x.%s", *requiredServiceBits, data.host); } @@ -1461,12 +1463,19 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe void CConnman::ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute + // Avoiding DNS seeds when we don't need them improves user privacy by + // creating fewer identifying DNS requests, reduces trust by giving seeds + // less influence on the network topology, and reduces traffic to the seeds. if ((addrman.size() > 0) && (!GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) { MilliSleep(11 * 1000); LOCK(cs_vNodes); - if (vNodes.size() >= 2) { + int nRelevant = 0; + for (auto pnode : vNodes) { + nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices); + } + if (nRelevant >= 2) { LogPrintf("P2P peers available. Skipped DNS seeding.\n"); return; } @@ -1621,7 +1630,6 @@ void CConnman::ThreadOpenConnections() } } } - assert(nOutbound <= (nMaxOutbound + nMaxFeeler)); // Feeler Connections // @@ -1674,8 +1682,8 @@ void CConnman::ThreadOpenConnections() if (nANow - addr.nLastTry < 600 && nTries < 30) continue; - // only consider nodes missing relevant services after 40 failed attempts - if ((addr.nServices & nRelevantServices) != nRelevantServices && nTries < 40) + // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up. + if ((addr.nServices & nRelevantServices) != nRelevantServices && (nTries < 40 || nOutbound >= (nMaxOutbound >> 1))) continue; // do not allow non-default ports, unless after 50 invalid addresses selected already @@ -503,7 +503,7 @@ public: bool fWhitelisted; double dPingTime; double dPingWait; - double dPingMin; + double dMinPing; std::string addrLocal; CAddress addr; }; @@ -735,14 +735,14 @@ public: addrKnown.insert(_addr.GetKey()); } - void PushAddress(const CAddress& _addr) + void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand) { // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { - vAddrToSend[insecure_rand() % vAddrToSend.size()] = _addr; + vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = _addr; } else { vAddrToSend.push_back(_addr); } diff --git a/src/netbase.cpp b/src/netbase.cpp index 7d7f1b6788..9fe34108f5 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -596,8 +596,8 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe // do socks negotiation if (proxy.randomize_credentials) { ProxyCredentials random_auth; - random_auth.username = strprintf("%i", insecure_rand()); - random_auth.password = strprintf("%i", insecure_rand()); + static std::atomic_int counter; + random_auth.username = random_auth.password = strprintf("%i", counter++); if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) return false; } else { diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 7b0e8b7d08..c07cd2eff8 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -594,7 +594,7 @@ FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee) CAmount FeeFilterRounder::round(CAmount currentMinFee) { std::set<double>::iterator it = feeset.lower_bound(currentMinFee); - if ((it != feeset.begin() && insecure_rand() % 3 != 0) || it == feeset.end()) { + if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) { it--; } return *it; diff --git a/src/policy/fees.h b/src/policy/fees.h index 463f62f710..2c1ac3b934 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -7,6 +7,7 @@ #include "amount.h" #include "uint256.h" +#include "random.h" #include <map> #include <string> @@ -298,5 +299,6 @@ public: private: std::set<double> feeset; + FastRandomContext insecure_rand; }; #endif /*BITCOIN_POLICYESTIMATOR_H */ diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 48080abc77..ae42b2bd74 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin developers +// Copyright (c) 2009-2016 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -154,6 +154,58 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return true; } +bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +{ + if (tx.IsCoinBase()) + return true; // Coinbases are skipped + + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + // We don't care if witness for this input is empty, since it must not be bloated. + // If the script is invalid without witness, it would be caught sooner or later during validation. + if (tx.wit.vtxinwit[i].IsNull()) + continue; + + const CTxOut &prev = mapInputs.GetOutputFor(tx.vin[i]); + + // get the scriptPubKey corresponding to this input: + CScript prevScript = prev.scriptPubKey; + + if (prevScript.IsPayToScriptHash()) { + std::vector <std::vector<unsigned char> > stack; + // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig + // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway. + // If the check fails at this stage, we know that this txid must be a bad one. + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE)) + return false; + if (stack.empty()) + return false; + prevScript = CScript(stack.back().begin(), stack.back().end()); + } + + int witnessversion = 0; + std::vector<unsigned char> witnessprogram; + + // Non-witness program must not be associated with any witness + if (!prevScript.IsWitnessProgram(witnessversion, witnessprogram)) + return false; + + // Check P2WSH standard limits + if (witnessversion == 0 && witnessprogram.size() == 32) { + if (tx.wit.vtxinwit[i].scriptWitness.stack.back().size() > MAX_STANDARD_P2WSH_SCRIPT_SIZE) + return false; + size_t sizeWitnessStack = tx.wit.vtxinwit[i].scriptWitness.stack.size() - 1; + if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS) + return false; + for (unsigned int j = 0; j < sizeWitnessStack; j++) { + if (tx.wit.vtxinwit[i].scriptWitness.stack[j].size() > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE) + return false; + } + } + } + return true; +} + unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost) diff --git a/src/policy/policy.h b/src/policy/policy.h index 9d6ff1233b..814e6c0b6f 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin developers +// Copyright (c) 2009-2016 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -30,6 +30,12 @@ static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5; static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; /** Default for -bytespersigop */ static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; +/** The maximum number of witness stack items in a standard P2WSH script */ +static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; +/** The maximum size of each witness stack item in a standard P2WSH script */ +static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; +/** The maximum size of a standard witnessScript */ +static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; /** * Standard script verification flags that standard transactions will comply * with. However scripts violating these flags may still be present in valid @@ -48,7 +54,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_WITNESS | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM | + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE; /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; @@ -69,6 +76,12 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes * @return True if all inputs (scriptSigs) use only standard transaction forms */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); + /** + * Check if the transaction is over standard P2WSH resources limit: + * 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements + * These limits are adequate for multi-signature up to n-of-100 using OP_CHECKSIG, OP_ADD, and OP_EQUAL, + */ +bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); extern unsigned int nBytesPerSigOp; diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 9dc641979e..8be4a955b3 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1353,13 +1353,36 @@ </widget> </item> <item row="16" column="0"> + <widget class="QLabel" name="peerMinPingLabel"> + <property name="text"> + <string>Min Ping</string> + </property> + </widget> + </item> + <item row="16" column="2"> + <widget class="QLabel" name="peerMinPing"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="17" column="0"> <widget class="QLabel" name="label_timeoffset"> <property name="text"> <string>Time Offset</string> </property> </widget> </item> - <item row="16" column="2"> + <item row="17" column="2"> <widget class="QLabel" name="timeoffset"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1375,7 +1398,7 @@ </property> </widget> </item> - <item row="17" column="1"> + <item row="18" column="1"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 923ed68996..710801ee96 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -20,7 +20,7 @@ <bool>false</bool> </property> <property name="styleSheet"> - <string notr="true">background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000;</string> + <string notr="true">QLabel { background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; }</string> </property> <property name="wordWrap"> <bool>true</bool> @@ -28,6 +28,9 @@ <property name="margin"> <number>3</number> </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByMouse</set> + </property> </widget> </item> <item> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 0beaddf997..42dafa1175 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -947,7 +947,7 @@ QString formatServicesStr(quint64 mask) QString formatPingTime(double dPingTime) { - return dPingTime == 0 ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10)); + return (dPingTime == std::numeric_limits<int64_t>::max()/1e6 || dPingTime == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10)); } QString formatTimeOffset(int64_t nTimeOffset) diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index a820bd791f..a2f9471fcc 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -31,7 +31,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: - return pLeft->dPingTime < pRight->dPingTime; + return pLeft->dMinPing < pRight->dMinPing; } return false; @@ -113,7 +113,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : clientModel(parent), timer(0) { - columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping Time"); + columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping"); priv = new PeerTablePriv(); // default to unsorted priv->sortColumn = -1; @@ -166,7 +166,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: - return GUIUtil::formatPingTime(rec->nodeStats.dPingTime); + return GUIUtil::formatPingTime(rec->nodeStats.dMinPing); } } else if (role == Qt::TextAlignmentRole) { if (index.column() == Ping) diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 5c6dc97b20..b50cad4975 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -43,18 +43,21 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid } // context menu actions + QAction *copyURIAction = new QAction(tr("Copy URI"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this); QAction *copyMessageAction = new QAction(tr("Copy message"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this); // context menu contextMenu = new QMenu(); + contextMenu->addAction(copyURIAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(copyMessageAction); contextMenu->addAction(copyAmountAction); // context menu signals connect(ui->recentRequestsView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint))); + connect(copyURIAction, SIGNAL(triggered()), this, SLOT(copyURI())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage())); connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); @@ -228,30 +231,50 @@ void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event) this->QDialog::keyPressEvent(event); } -// copy column of selected row to clipboard -void ReceiveCoinsDialog::copyColumnToClipboard(int column) +QModelIndex ReceiveCoinsDialog::selectedRow() { if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) - return; + return QModelIndex(); QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); if(selection.empty()) - return; + return QModelIndex(); // correct for selection mode ContiguousSelection QModelIndex firstIndex = selection.at(0); + return firstIndex; +} + +// copy column of selected row to clipboard +void ReceiveCoinsDialog::copyColumnToClipboard(int column) +{ + QModelIndex firstIndex = selectedRow(); + if (!firstIndex.isValid()) { + return; + } GUIUtil::setClipboard(model->getRecentRequestsTableModel()->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole).toString()); } // context menu void ReceiveCoinsDialog::showMenu(const QPoint &point) { - if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) - return; - QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); - if(selection.empty()) + if (!selectedRow().isValid()) { return; + } contextMenu->exec(QCursor::pos()); } +// context menu action: copy URI +void ReceiveCoinsDialog::copyURI() +{ + QModelIndex sel = selectedRow(); + if (!sel.isValid()) { + return; + } + + const RecentRequestsTableModel * const submodel = model->getRecentRequestsTableModel(); + const QString uri = GUIUtil::formatBitcoinURI(submodel->entry(sel.row()).recipient); + GUIUtil::setClipboard(uri); +} + // context menu action: copy label void ReceiveCoinsDialog::copyLabel() { diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 226fd65cfa..d137f1616e 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -60,6 +60,7 @@ private: QMenu *contextMenu; const PlatformStyle *platformStyle; + QModelIndex selectedRow(); void copyColumnToClipboard(int column); virtual void resizeEvent(QResizeEvent *event); @@ -71,6 +72,7 @@ private Q_SLOTS: void recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void updateDisplayUnit(); void showMenu(const QPoint &point); + void copyURI(); void copyLabel(); void copyMessage(); void copyAmount(); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index b6ed75535c..f10dddf589 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -246,7 +246,10 @@ bool RPCConsole::RPCExecuteCommandLine(std::string &strResult, const std::string std::string strPrint; // Convert argument list to JSON objects in method-dependent way, // and pass it along with the method name to the dispatcher. - lastResult = tableRPC.execute(stack.back()[0], RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()))); + JSONRPCRequest req; + req.params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end())); + req.strMethod = stack.back()[0]; + lastResult = tableRPC.execute(req); state = STATE_COMMAND_EXECUTED; curarg.clear(); @@ -895,6 +898,7 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected)); ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait)); + ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing)); ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion))); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); 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..305cb4fefa 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -563,6 +563,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) { diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index b105c6d991..cdac60da36 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -187,6 +187,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; diff --git a/src/random.cpp b/src/random.cpp index d9a8cc145e..aa027e49c4 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -178,22 +178,21 @@ uint256 GetRandHash() return hash; } -uint32_t insecure_rand_Rz = 11; -uint32_t insecure_rand_Rw = 11; -void seed_insecure_rand(bool fDeterministic) +FastRandomContext::FastRandomContext(bool fDeterministic) { // The seed values have some unlikely fixed points which we avoid. if (fDeterministic) { - insecure_rand_Rz = insecure_rand_Rw = 11; + Rz = Rw = 11; } else { uint32_t tmp; do { GetRandBytes((unsigned char*)&tmp, 4); } while (tmp == 0 || tmp == 0x9068ffffU); - insecure_rand_Rz = tmp; + Rz = tmp; do { GetRandBytes((unsigned char*)&tmp, 4); } while (tmp == 0 || tmp == 0x464fffffU); - insecure_rand_Rw = tmp; + Rw = tmp; } } + diff --git a/src/random.h b/src/random.h index 31b80bd565..e97d2d1fb0 100644 --- a/src/random.h +++ b/src/random.h @@ -28,25 +28,22 @@ uint256 GetRandHash(); void GetStrongRandBytes(unsigned char* buf, int num); /** - * Seed insecure_rand using the random pool. - * @param Deterministic Use a deterministic seed + * Fast randomness source. This is seeded once with secure random data, but + * is completely deterministic and insecure after that. + * This class is not thread-safe. */ -void seed_insecure_rand(bool fDeterministic = false); - -/** - * MWC RNG of George Marsaglia - * This is intended to be fast. It has a period of 2^59.3, though the - * least significant 16 bits only have a period of about 2^30.1. - * - * @return random value - */ -extern uint32_t insecure_rand_Rz; -extern uint32_t insecure_rand_Rw; -static inline uint32_t insecure_rand(void) -{ - insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16); - insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16); - return (insecure_rand_Rw << 16) + insecure_rand_Rz; -} +class FastRandomContext { +public: + explicit FastRandomContext(bool fDeterministic=false); + + uint32_t rand32() { + Rz = 36969 * (Rz & 65535) + (Rz >> 16); + Rw = 18000 * (Rw & 65535) + (Rw >> 16); + return (Rw << 16) + Rz; + } + + uint32_t Rz; + uint32_t Rw; +}; #endif // BITCOIN_RANDOM_H diff --git a/src/rest.cpp b/src/rest.cpp index c815592124..b8b5420626 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -274,7 +274,7 @@ static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPa } // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp -UniValue getblockchaininfo(const UniValue& params, bool fHelp); +UniValue getblockchaininfo(const JSONRPCRequest& request); static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) { @@ -285,8 +285,9 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) switch (rf) { case RF_JSON: { - UniValue rpcParams(UniValue::VARR); - UniValue chainInfoObject = getblockchaininfo(rpcParams, false); + JSONRPCRequest jsonRequest; + jsonRequest.params = UniValue(UniValue::VARR); + UniValue chainInfoObject = getblockchaininfo(jsonRequest); string strJSON = chainInfoObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1e04fcc01c..f538ddcc04 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -146,9 +146,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx return result; } -UniValue getblockcount(const UniValue& params, bool fHelp) +UniValue getblockcount(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getblockcount\n" "\nReturns the number of blocks in the longest block chain.\n" @@ -163,9 +163,9 @@ UniValue getblockcount(const UniValue& params, bool fHelp) return chainActive.Height(); } -UniValue getbestblockhash(const UniValue& params, bool fHelp) +UniValue getbestblockhash(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + 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" @@ -190,9 +190,9 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex) cond_blockchange.notify_all(); } -UniValue waitfornewblock(const UniValue& params, bool fHelp) +UniValue waitfornewblock(const JSONRPCRequest& request) { - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( "waitfornewblock\n" "\nWaits for a specific new block and returns useful info about it.\n" @@ -209,8 +209,8 @@ UniValue waitfornewblock(const UniValue& params, bool fHelp) + HelpExampleRpc("waitfornewblock", "1000") ); int timeout = 0; - if (params.size() > 0) - timeout = params[0].get_int(); + if (request.params.size() > 0) + timeout = request.params[0].get_int(); CUpdatedBlock block; { @@ -228,9 +228,9 @@ UniValue waitfornewblock(const UniValue& params, bool fHelp) return ret; } -UniValue waitforblock(const UniValue& params, bool fHelp) +UniValue waitforblock(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "waitforblock\n" "\nWaits for a specific new block and returns useful info about it.\n" @@ -249,10 +249,10 @@ UniValue waitforblock(const UniValue& params, bool fHelp) ); int timeout = 0; - uint256 hash = uint256S(params[0].get_str()); + uint256 hash = uint256S(request.params[0].get_str()); - if (params.size() > 1) - timeout = params[1].get_int(); + if (request.params.size() > 1) + timeout = request.params[1].get_int(); CUpdatedBlock block; { @@ -270,9 +270,9 @@ UniValue waitforblock(const UniValue& params, bool fHelp) return ret; } -UniValue waitforblockheight(const UniValue& params, bool fHelp) +UniValue waitforblockheight(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "waitforblock\n" "\nWaits for (at least) block height and returns the height and hash\n" @@ -292,10 +292,10 @@ UniValue waitforblockheight(const UniValue& params, bool fHelp) ); int timeout = 0; - int height = params[0].get_int(); + int height = request.params[0].get_int(); - if (params.size() > 1) - timeout = params[1].get_int(); + if (request.params.size() > 1) + timeout = request.params[1].get_int(); CUpdatedBlock block; { @@ -312,9 +312,9 @@ UniValue waitforblockheight(const UniValue& params, bool fHelp) return ret; } -UniValue getdifficulty(const UniValue& params, bool fHelp) +UniValue getdifficulty(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getdifficulty\n" "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" @@ -411,9 +411,9 @@ UniValue mempoolToJSON(bool fVerbose = false) } } -UniValue getrawmempool(const UniValue& params, bool fHelp) +UniValue getrawmempool(const JSONRPCRequest& request) { - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( "getrawmempool ( verbose )\n" "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" @@ -436,15 +436,15 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) ); bool fVerbose = false; - if (params.size() > 0) - fVerbose = params[0].get_bool(); + if (request.params.size() > 0) + fVerbose = request.params[0].get_bool(); return mempoolToJSON(fVerbose); } -UniValue getmempoolancestors(const UniValue& params, bool fHelp) +UniValue getmempoolancestors(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) { + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw runtime_error( "getmempoolancestors txid (verbose)\n" "\nIf txid is in the mempool, returns all in-mempool ancestors.\n" @@ -469,10 +469,10 @@ UniValue getmempoolancestors(const UniValue& params, bool fHelp) } bool fVerbose = false; - if (params.size() > 1) - fVerbose = params[1].get_bool(); + if (request.params.size() > 1) + fVerbose = request.params[1].get_bool(); - uint256 hash = ParseHashV(params[0], "parameter 1"); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(mempool.cs); @@ -506,9 +506,9 @@ UniValue getmempoolancestors(const UniValue& params, bool fHelp) } } -UniValue getmempooldescendants(const UniValue& params, bool fHelp) +UniValue getmempooldescendants(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) { + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw runtime_error( "getmempooldescendants txid (verbose)\n" "\nIf txid is in the mempool, returns all in-mempool descendants.\n" @@ -533,10 +533,10 @@ UniValue getmempooldescendants(const UniValue& params, bool fHelp) } bool fVerbose = false; - if (params.size() > 1) - fVerbose = params[1].get_bool(); + if (request.params.size() > 1) + fVerbose = request.params[1].get_bool(); - uint256 hash = ParseHashV(params[0], "parameter 1"); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(mempool.cs); @@ -570,9 +570,9 @@ UniValue getmempooldescendants(const UniValue& params, bool fHelp) } } -UniValue getmempoolentry(const UniValue& params, bool fHelp) +UniValue getmempoolentry(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) { + if (request.fHelp || request.params.size() != 1) { throw runtime_error( "getmempoolentry txid\n" "\nReturns mempool data for given transaction\n" @@ -588,7 +588,7 @@ UniValue getmempoolentry(const UniValue& params, bool fHelp) ); } - uint256 hash = ParseHashV(params[0], "parameter 1"); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); LOCK(mempool.cs); @@ -603,9 +603,9 @@ UniValue getmempoolentry(const UniValue& params, bool fHelp) return info; } -UniValue getblockhash(const UniValue& params, bool fHelp) +UniValue getblockhash(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "getblockhash index\n" "\nReturns hash of block in best-block-chain at index provided.\n" @@ -620,7 +620,7 @@ UniValue getblockhash(const UniValue& params, bool fHelp) LOCK(cs_main); - int nHeight = params[0].get_int(); + int nHeight = request.params[0].get_int(); if (nHeight < 0 || nHeight > chainActive.Height()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -628,9 +628,9 @@ UniValue getblockhash(const UniValue& params, bool fHelp) return pblockindex->GetBlockHash().GetHex(); } -UniValue getblockheader(const UniValue& params, bool fHelp) +UniValue getblockheader(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getblockheader \"hash\" ( verbose )\n" "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n" @@ -664,12 +664,12 @@ UniValue getblockheader(const UniValue& params, bool fHelp) LOCK(cs_main); - std::string strHash = params[0].get_str(); + std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); + if (request.params.size() > 1) + fVerbose = request.params[1].get_bool(); if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -687,9 +687,9 @@ UniValue getblockheader(const UniValue& params, bool fHelp) return blockheaderToJSON(pblockindex); } -UniValue getblock(const UniValue& params, bool fHelp) +UniValue getblock(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getblock \"hash\" ( verbose )\n" "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n" @@ -730,12 +730,12 @@ UniValue getblock(const UniValue& params, bool fHelp) LOCK(cs_main); - std::string strHash = params[0].get_str(); + std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); bool fVerbose = true; - if (params.size() > 1) - fVerbose = params[1].get_bool(); + if (request.params.size() > 1) + fVerbose = request.params[1].get_bool(); if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -814,9 +814,9 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) return true; } -UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) +UniValue gettxoutsetinfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "gettxoutsetinfo\n" "\nReturns statistics about the unspent transaction output set.\n" @@ -854,9 +854,9 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) return ret; } -UniValue gettxout(const UniValue& params, bool fHelp) +UniValue gettxout(const JSONRPCRequest& request) { - if (fHelp || params.size() < 2 || params.size() > 3) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw runtime_error( "gettxout \"txid\" n ( includemempool )\n" "\nReturns details about an unspent transaction output.\n" @@ -896,12 +896,12 @@ UniValue gettxout(const UniValue& params, bool fHelp) UniValue ret(UniValue::VOBJ); - std::string strHash = params[0].get_str(); + std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); - int n = params[1].get_int(); + int n = request.params[1].get_int(); bool fMempool = true; - if (params.size() > 2) - fMempool = params[2].get_bool(); + if (request.params.size() > 2) + fMempool = request.params[2].get_bool(); CCoins coins; if (fMempool) { @@ -934,11 +934,11 @@ UniValue gettxout(const UniValue& params, bool fHelp) return ret; } -UniValue verifychain(const UniValue& params, bool fHelp) +UniValue verifychain(const JSONRPCRequest& request) { int nCheckLevel = GetArg("-checklevel", DEFAULT_CHECKLEVEL); int nCheckDepth = GetArg("-checkblocks", DEFAULT_CHECKBLOCKS); - if (fHelp || params.size() > 2) + if (request.fHelp || request.params.size() > 2) throw runtime_error( "verifychain ( checklevel numblocks )\n" "\nVerifies blockchain database.\n" @@ -954,10 +954,10 @@ UniValue verifychain(const UniValue& params, bool fHelp) LOCK(cs_main); - if (params.size() > 0) - nCheckLevel = params[0].get_int(); - if (params.size() > 1) - nCheckDepth = params[1].get_int(); + if (request.params.size() > 0) + nCheckLevel = request.params[0].get_int(); + if (request.params.size() > 1) + nCheckDepth = request.params[1].get_int(); return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); } @@ -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; } @@ -1021,9 +1022,9 @@ void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name, bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id))); } -UniValue getblockchaininfo(const UniValue& params, bool fHelp) +UniValue getblockchaininfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getblockchaininfo\n" "Returns an object containing various state info regarding block chain processing.\n" @@ -1053,7 +1054,8 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " \"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" @@ -1113,9 +1115,9 @@ struct CompareBlocksByHeight } }; -UniValue getchaintips(const UniValue& params, bool fHelp) +UniValue getchaintips(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getchaintips\n" "Return information about all known tips in the block tree," @@ -1229,9 +1231,9 @@ UniValue mempoolInfoToJSON() return ret; } -UniValue getmempoolinfo(const UniValue& params, bool fHelp) +UniValue getmempoolinfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getmempoolinfo\n" "\nReturns details on the active state of the TX memory pool.\n" @@ -1251,9 +1253,47 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp) return mempoolInfoToJSON(); } -UniValue invalidateblock(const UniValue& params, bool fHelp) +UniValue preciousblock(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) + throw runtime_error( + "preciousblock \"hash\"\n" + "\nTreats a block as if it were received before others with the same work.\n" + "\nA later preciousblock call can override the effect of an earlier one.\n" + "\nThe effects of preciousblock are not retained across restarts.\n" + "\nArguments:\n" + "1. hash (string, required) the hash of the block to mark as precious\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("preciousblock", "\"blockhash\"") + + HelpExampleRpc("preciousblock", "\"blockhash\"") + ); + + std::string strHash = request.params[0].get_str(); + uint256 hash(uint256S(strHash)); + CBlockIndex* pblockindex; + + { + LOCK(cs_main); + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + pblockindex = mapBlockIndex[hash]; + } + + CValidationState state; + PreciousBlock(state, Params(), pblockindex); + + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } + + return NullUniValue; +} + +UniValue invalidateblock(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) throw runtime_error( "invalidateblock \"hash\"\n" "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" @@ -1265,7 +1305,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) + HelpExampleRpc("invalidateblock", "\"blockhash\"") ); - std::string strHash = params[0].get_str(); + std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); CValidationState state; @@ -1279,7 +1319,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) } if (state.IsValid()) { - ActivateBestChain(state, Params(), NULL, g_connman.get()); + ActivateBestChain(state, Params(), NULL); } if (!state.IsValid()) { @@ -1289,9 +1329,9 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue reconsiderblock(const UniValue& params, bool fHelp) +UniValue reconsiderblock(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "reconsiderblock \"hash\"\n" "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" @@ -1304,7 +1344,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp) + HelpExampleRpc("reconsiderblock", "\"blockhash\"") ); - std::string strHash = params[0].get_str(); + std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); { @@ -1317,7 +1357,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp) } CValidationState state; - ActivateBestChain(state, Params(), NULL, g_connman.get()); + ActivateBestChain(state, Params(), NULL); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); @@ -1346,6 +1386,8 @@ static const CRPCCommand commands[] = { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, { "blockchain", "verifychain", &verifychain, true }, + { "blockchain", "preciousblock", &preciousblock, true }, + /* Not shown in help */ { "hidden", "invalidateblock", &invalidateblock, true }, { "hidden", "reconsiderblock", &reconsiderblock, true }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 6b13aa5bab..d509dd691f 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -22,6 +22,7 @@ #include "utilstrencodings.h" #include "validationinterface.h" +#include <memory> #include <stdint.h> #include <boost/assign/list_of.hpp> @@ -73,9 +74,9 @@ UniValue GetNetworkHashPS(int lookup, int height) { return workDiff.getdouble() / timeDiff; } -UniValue getnetworkhashps(const UniValue& params, bool fHelp) +UniValue getnetworkhashps(const JSONRPCRequest& request) { - if (fHelp || params.size() > 2) + if (request.fHelp || request.params.size() > 2) throw runtime_error( "getnetworkhashps ( blocks height )\n" "\nReturns the estimated network hashes per second based on the last n blocks.\n" @@ -92,7 +93,7 @@ UniValue getnetworkhashps(const UniValue& params, bool fHelp) ); LOCK(cs_main); - return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); + return GetNetworkHashPS(request.params.size() > 0 ? request.params[0].get_int() : 120, request.params.size() > 1 ? request.params[1].get_int() : -1); } UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript) @@ -131,7 +132,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG continue; } CValidationState state; - if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, g_connman.get())) + if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); @@ -145,9 +146,9 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG return blockHashes; } -UniValue generate(const UniValue& params, bool fHelp) +UniValue generate(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "generate numblocks ( maxtries )\n" "\nMine up to numblocks blocks immediately (before the RPC call returns)\n" @@ -161,10 +162,10 @@ UniValue generate(const UniValue& params, bool fHelp) + HelpExampleCli("generate", "11") ); - int nGenerate = params[0].get_int(); + int nGenerate = request.params[0].get_int(); uint64_t nMaxTries = 1000000; - if (params.size() > 1) { - nMaxTries = params[1].get_int(); + if (request.params.size() > 1) { + nMaxTries = request.params[1].get_int(); } boost::shared_ptr<CReserveScript> coinbaseScript; @@ -181,9 +182,9 @@ UniValue generate(const UniValue& params, bool fHelp) return generateBlocks(coinbaseScript, nGenerate, nMaxTries, true); } -UniValue generatetoaddress(const UniValue& params, bool fHelp) +UniValue generatetoaddress(const JSONRPCRequest& request) { - if (fHelp || params.size() < 2 || params.size() > 3) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw runtime_error( "generatetoaddress numblocks address (maxtries)\n" "\nMine blocks immediately to a specified address (before the RPC call returns)\n" @@ -198,13 +199,13 @@ UniValue generatetoaddress(const UniValue& params, bool fHelp) + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") ); - int nGenerate = params[0].get_int(); + int nGenerate = request.params[0].get_int(); uint64_t nMaxTries = 1000000; - if (params.size() > 2) { - nMaxTries = params[2].get_int(); + if (request.params.size() > 2) { + nMaxTries = request.params[2].get_int(); } - CBitcoinAddress address(params[1].get_str()); + CBitcoinAddress address(request.params[1].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); @@ -214,9 +215,9 @@ UniValue generatetoaddress(const UniValue& params, bool fHelp) return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false); } -UniValue getmininginfo(const UniValue& params, bool fHelp) +UniValue getmininginfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getmininginfo\n" "\nReturns a json object containing mining-related information." @@ -230,7 +231,6 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) " \"errors\": \"...\" (string) Current errors\n" " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mem pool\n" - " \"testnet\": true|false (boolean) If using testnet or not\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" "}\n" "\nExamples:\n" @@ -248,18 +248,17 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); + obj.push_back(Pair("networkhashps", getnetworkhashps(request))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); obj.push_back(Pair("chain", Params().NetworkIDString())); return obj; } // NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts -UniValue prioritisetransaction(const UniValue& params, bool fHelp) +UniValue prioritisetransaction(const JSONRPCRequest& request) { - if (fHelp || params.size() != 3) + if (request.fHelp || request.params.size() != 3) throw runtime_error( "prioritisetransaction <txid> <priority delta> <fee delta>\n" "Accepts the transaction into mined blocks at a higher (or lower) priority\n" @@ -280,10 +279,10 @@ UniValue prioritisetransaction(const UniValue& params, bool fHelp) LOCK(cs_main); - uint256 hash = ParseHashStr(params[0].get_str(), "txid"); - CAmount nAmount = params[2].get_int64(); + uint256 hash = ParseHashStr(request.params[0].get_str(), "txid"); + CAmount nAmount = request.params[2].get_int64(); - mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); + mempool.PrioritiseTransaction(hash, request.params[0].get_str(), request.params[1].get_real(), nAmount); return true; } @@ -316,71 +315,77 @@ std::string gbt_vb_name(const Consensus::DeploymentPos pos) { return s; } -UniValue getblocktemplate(const UniValue& params, bool fHelp) +UniValue getblocktemplate(const JSONRPCRequest& request) { - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( - "getblocktemplate ( \"jsonrequestobject\" )\n" + "getblocktemplate ( TemplateRequest )\n" "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" "It returns data needed to construct a block to work on.\n" - "For full specification, see BIPs 22 and 9:\n" + "For full specification, see BIPs 22, 23, 9, and 145:\n" " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" + " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n" "\nArguments:\n" - "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" + "1. TemplateRequest (json object, optional) A json object in the following spec\n" " {\n" - " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n" - " \"capabilities\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" + " \"mode\":\"template\" (string, optional) This must be set to \"template\", \"proposal\" (see BIP 23), or omitted\n" + " \"capabilities\":[ (array, optional) A list of strings\n" + " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" " ,...\n" - " ]\n" + " ],\n" + " \"rules\":[ (array, optional) A list of strings\n" + " \"support\" (string) client side supported softfork deployment\n" + " ,...\n" + " ]\n" " }\n" "\n" "\nResult:\n" "{\n" - " \"version\" : n, (numeric) The block version\n" + " \"version\" : n, (numeric) The preferred block version\n" " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n" - " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" + " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" " ,...\n" " },\n" " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" - " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" + " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" - " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" - " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" - " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n" - " \"depends\" : [ (array) array of numbers \n" - " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" + " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" + " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" + " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n" + " \"depends\" : [ (array) array of numbers \n" + " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" - " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" - " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" - " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" + " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" + " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" " }\n" " ,...\n" " ],\n" - " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" - " \"flags\" : \"flags\" (string) \n" + " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" + " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n" " },\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" - " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" - " \"target\" : \"xxxx\", (string) The hash target\n" - " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" - " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" + " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" + " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" + " \"target\" : \"xxxx\", (string) The hash target\n" + " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" + " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" " ,...\n" " ],\n" - " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n" - " \"sigoplimit\" : n, (numeric) cost limit of sigops in blocks\n" + " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid nonces\n" + " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" " \"sizelimit\" : n, (numeric) limit of block size\n" " \"weightlimit\" : n, (numeric) limit of block weight\n" " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" - " \"bits\" : \"xxx\", (string) compressed target of next block\n" + " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n" " \"height\" : n (numeric) The height of the next block\n" "}\n" @@ -395,9 +400,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue lpval = NullUniValue; std::set<std::string> setClientRules; int64_t nMaxVersionPreVB = -1; - if (params.size() > 0) + if (request.params.size() > 0) { - const UniValue& oparam = params[0].get_obj(); + const UniValue& oparam = request.params[0].get_obj(); const UniValue& modeval = find_value(oparam, "mode"); if (modeval.isStr()) strMode = modeval.get_str(); @@ -517,12 +522,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) // Update block static CBlockIndex* pindexPrev; static int64_t nStart; - static CBlockTemplate* pblocktemplate; + static std::unique_ptr<CBlockTemplate> pblocktemplate; if (pindexPrev != chainActive.Tip() || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on - pindexPrev = NULL; + pindexPrev = nullptr; // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); @@ -530,11 +535,6 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) nStart = GetTime(); // Create new block - if(pblocktemplate) - { - delete pblocktemplate; - pblocktemplate = NULL; - } CScript scriptDummy = CScript() << OP_TRUE; pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy); if (!pblocktemplate) @@ -705,9 +705,9 @@ protected: }; }; -UniValue submitblock(const UniValue& params, bool fHelp) +UniValue submitblock(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "submitblock \"hexdata\" ( \"jsonparametersobject\" )\n" "\nAttempts to submit new block to network.\n" @@ -727,7 +727,7 @@ UniValue submitblock(const UniValue& params, bool fHelp) ); CBlock block; - if (!DecodeHexBlk(block, params[0].get_str())) + if (!DecodeHexBlk(block, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); uint256 hash = block.GetHash(); @@ -757,7 +757,7 @@ UniValue submitblock(const UniValue& params, bool fHelp) CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, g_connman.get()); + bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); UnregisterValidationInterface(&sc); if (fBlockPresent) { @@ -774,9 +774,9 @@ UniValue submitblock(const UniValue& params, bool fHelp) return BIP22ValidationResult(state); } -UniValue estimatefee(const UniValue& params, bool fHelp) +UniValue estimatefee(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "estimatefee nblocks\n" "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" @@ -792,9 +792,9 @@ UniValue estimatefee(const UniValue& params, bool fHelp) + HelpExampleCli("estimatefee", "6") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)); - int nBlocks = params[0].get_int(); + int nBlocks = request.params[0].get_int(); if (nBlocks < 1) nBlocks = 1; @@ -805,9 +805,9 @@ UniValue estimatefee(const UniValue& params, bool fHelp) return ValueFromAmount(feeRate.GetFeePerK()); } -UniValue estimatepriority(const UniValue& params, bool fHelp) +UniValue estimatepriority(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "estimatepriority nblocks\n" "\nEstimates the approximate priority a zero-fee transaction needs to begin\n" @@ -823,18 +823,18 @@ UniValue estimatepriority(const UniValue& params, bool fHelp) + HelpExampleCli("estimatepriority", "6") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)); - int nBlocks = params[0].get_int(); + int nBlocks = request.params[0].get_int(); if (nBlocks < 1) nBlocks = 1; return mempool.estimatePriority(nBlocks); } -UniValue estimatesmartfee(const UniValue& params, bool fHelp) +UniValue estimatesmartfee(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "estimatesmartfee nblocks\n" "\nWARNING: This interface is unstable and may disappear or change!\n" @@ -856,9 +856,9 @@ UniValue estimatesmartfee(const UniValue& params, bool fHelp) + HelpExampleCli("estimatesmartfee", "6") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)); - int nBlocks = params[0].get_int(); + int nBlocks = request.params[0].get_int(); UniValue result(UniValue::VOBJ); int answerFound; @@ -868,9 +868,9 @@ UniValue estimatesmartfee(const UniValue& params, bool fHelp) return result; } -UniValue estimatesmartpriority(const UniValue& params, bool fHelp) +UniValue estimatesmartpriority(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "estimatesmartpriority nblocks\n" "\nWARNING: This interface is unstable and may disappear or change!\n" @@ -892,9 +892,9 @@ UniValue estimatesmartpriority(const UniValue& params, bool fHelp) + HelpExampleCli("estimatesmartpriority", "6") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)); - int nBlocks = params[0].get_int(); + int nBlocks = request.params[0].get_int(); UniValue result(UniValue::VOBJ); int answerFound; diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index f0b7e0a07c..eaef4856b3 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -39,9 +39,9 @@ using namespace std; * * Or alternatively, create a specific query method for the information. **/ -UniValue getinfo(const UniValue& params, bool fHelp) +UniValue getinfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getinfo\n" "\nDEPRECATED. Returns an object containing various state info.\n" @@ -93,7 +93,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); + obj.push_back(Pair("testnet", Params().NetworkIDString() == CBaseChainParams::TESTNET)); #ifdef ENABLE_WALLET if (pwalletMain) { obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); @@ -148,9 +148,9 @@ public: }; #endif -UniValue validateaddress(const UniValue& params, bool fHelp) +UniValue validateaddress(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "validateaddress \"bitcoinaddress\"\n" "\nReturn information about the given bitcoin address.\n" @@ -181,7 +181,7 @@ UniValue validateaddress(const UniValue& params, bool fHelp) LOCK(cs_main); #endif - CBitcoinAddress address(params[0].get_str()); + CBitcoinAddress address(request.params[0].get_str()); bool isValid = address.IsValid(); UniValue ret(UniValue::VOBJ); @@ -278,9 +278,9 @@ CScript _createmultisig_redeemScript(const UniValue& params) return result; } -UniValue createmultisig(const UniValue& params, bool fHelp) +UniValue createmultisig(const JSONRPCRequest& request) { - if (fHelp || params.size() < 2 || params.size() > 2) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) { string msg = "createmultisig nrequired [\"key\",...]\n" "\nCreates a multi-signature address with n signature of m keys required.\n" @@ -310,7 +310,7 @@ UniValue createmultisig(const UniValue& params, bool fHelp) } // Construct using pay-to-script-hash: - CScript inner = _createmultisig_redeemScript(params); + CScript inner = _createmultisig_redeemScript(request.params); CScriptID innerID(inner); CBitcoinAddress address(innerID); @@ -321,9 +321,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp) return result; } -UniValue verifymessage(const UniValue& params, bool fHelp) +UniValue verifymessage(const JSONRPCRequest& request) { - if (fHelp || params.size() != 3) + if (request.fHelp || request.params.size() != 3) throw runtime_error( "verifymessage \"bitcoinaddress\" \"signature\" \"message\"\n" "\nVerify a signed message\n" @@ -346,9 +346,9 @@ UniValue verifymessage(const UniValue& params, bool fHelp) LOCK(cs_main); - string strAddress = params[0].get_str(); - string strSign = params[1].get_str(); - string strMessage = params[2].get_str(); + string strAddress = request.params[0].get_str(); + string strSign = request.params[1].get_str(); + string strMessage = request.params[2].get_str(); CBitcoinAddress addr(strAddress); if (!addr.IsValid()) @@ -375,9 +375,9 @@ UniValue verifymessage(const UniValue& params, bool fHelp) return (pubkey.GetID() == keyID); } -UniValue signmessagewithprivkey(const UniValue& params, bool fHelp) +UniValue signmessagewithprivkey(const JSONRPCRequest& request) { - if (fHelp || params.size() != 2) + if (request.fHelp || request.params.size() != 2) throw runtime_error( "signmessagewithprivkey \"privkey\" \"message\"\n" "\nSign a message with the private key of an address\n" @@ -395,8 +395,8 @@ UniValue signmessagewithprivkey(const UniValue& params, bool fHelp) + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"") ); - string strPrivkey = params[0].get_str(); - string strMessage = params[1].get_str(); + string strPrivkey = request.params[0].get_str(); + string strMessage = request.params[1].get_str(); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strPrivkey); @@ -417,9 +417,9 @@ UniValue signmessagewithprivkey(const UniValue& params, bool fHelp) return EncodeBase64(&vchSig[0], vchSig.size()); } -UniValue setmocktime(const UniValue& params, bool fHelp) +UniValue setmocktime(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "setmocktime timestamp\n" "\nSet the local time to given timestamp (-regtest only)\n" @@ -437,8 +437,8 @@ UniValue setmocktime(const UniValue& params, bool fHelp) // in a long time. LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - SetMockTime(params[0].get_int64()); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)); + SetMockTime(request.params[0].get_int64()); uint64_t t = GetTime(); if(g_connman) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index b011029f51..2b43f08f0b 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -23,9 +23,9 @@ using namespace std; -UniValue getconnectioncount(const UniValue& params, bool fHelp) +UniValue getconnectioncount(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getconnectioncount\n" "\nReturns the number of connections to other nodes.\n" @@ -42,9 +42,9 @@ UniValue getconnectioncount(const UniValue& params, bool fHelp) return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL); } -UniValue ping(const UniValue& params, bool fHelp) +UniValue ping(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "ping\n" "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" @@ -65,9 +65,9 @@ UniValue ping(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue getpeerinfo(const UniValue& params, bool fHelp) +UniValue getpeerinfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getpeerinfo\n" "\nReturns data about each connected network node as a json array of objects.\n" @@ -141,8 +141,8 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("timeoffset", stats.nTimeOffset)); if (stats.dPingTime > 0.0) obj.push_back(Pair("pingtime", stats.dPingTime)); - if (stats.dPingMin < std::numeric_limits<int64_t>::max()/1e6) - obj.push_back(Pair("minping", stats.dPingMin)); + if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6) + obj.push_back(Pair("minping", stats.dMinPing)); if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); @@ -184,12 +184,12 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) return ret; } -UniValue addnode(const UniValue& params, bool fHelp) +UniValue addnode(const JSONRPCRequest& request) { string strCommand; - if (params.size() == 2) - strCommand = params[1].get_str(); - if (fHelp || params.size() != 2 || + if (request.params.size() == 2) + strCommand = request.params[1].get_str(); + if (request.fHelp || request.params.size() != 2 || (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) throw runtime_error( "addnode \"node\" \"add|remove|onetry\"\n" @@ -206,7 +206,7 @@ UniValue addnode(const UniValue& params, bool fHelp) if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - string strNode = params[0].get_str(); + string strNode = request.params[0].get_str(); if (strCommand == "onetry") { @@ -229,9 +229,9 @@ UniValue addnode(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue disconnectnode(const UniValue& params, bool fHelp) +UniValue disconnectnode(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "disconnectnode \"node\" \n" "\nImmediately disconnects from the specified node.\n" @@ -245,16 +245,16 @@ UniValue disconnectnode(const UniValue& params, bool fHelp) if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - bool ret = g_connman->DisconnectNode(params[0].get_str()); + bool ret = g_connman->DisconnectNode(request.params[0].get_str()); if (!ret) throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); return NullUniValue; } -UniValue getaddednodeinfo(const UniValue& params, bool fHelp) +UniValue getaddednodeinfo(const JSONRPCRequest& request) { - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( "getaddednodeinfo ( \"node\" )\n" "\nReturns information about the given added node, or all added nodes\n" @@ -286,10 +286,10 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) std::vector<AddedNodeInfo> vInfo = g_connman->GetAddedNodeInfo(); - if (params.size() == 1) { + if (request.params.size() == 1) { bool found = false; for (const AddedNodeInfo& info : vInfo) { - if (info.strAddedNode == params[0].get_str()) { + if (info.strAddedNode == request.params[0].get_str()) { vInfo.assign(1, info); found = true; break; @@ -320,9 +320,9 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) return ret; } -UniValue getnettotals(const UniValue& params, bool fHelp) +UniValue getnettotals(const JSONRPCRequest& request) { - if (fHelp || params.size() > 0) + if (request.fHelp || request.params.size() > 0) throw runtime_error( "getnettotals\n" "\nReturns information about network traffic, including bytes in, bytes out,\n" @@ -386,9 +386,9 @@ static UniValue GetNetworksInfo() return networks; } -UniValue getnetworkinfo(const UniValue& params, bool fHelp) +UniValue getnetworkinfo(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getnetworkinfo\n" "Returns an object containing various state info regarding P2P networking.\n" @@ -456,12 +456,12 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) return obj; } -UniValue setban(const UniValue& params, bool fHelp) +UniValue setban(const JSONRPCRequest& request) { string strCommand; - if (params.size() >= 2) - strCommand = params[1].get_str(); - if (fHelp || params.size() < 2 || + if (request.params.size() >= 2) + strCommand = request.params[1].get_str(); + if (request.fHelp || request.params.size() < 2 || (strCommand != "add" && strCommand != "remove")) throw runtime_error( "setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n" @@ -483,16 +483,16 @@ UniValue setban(const UniValue& params, bool fHelp) CNetAddr netAddr; bool isSubnet = false; - if (params[0].get_str().find("/") != string::npos) + if (request.params[0].get_str().find("/") != string::npos) isSubnet = true; if (!isSubnet) { CNetAddr resolved; - LookupHost(params[0].get_str().c_str(), resolved, false); + LookupHost(request.params[0].get_str().c_str(), resolved, false); netAddr = resolved; } else - LookupSubNet(params[0].get_str().c_str(), subNet); + LookupSubNet(request.params[0].get_str().c_str(), subNet); if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet"); @@ -503,11 +503,11 @@ UniValue setban(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); int64_t banTime = 0; //use standard bantime if not specified - if (params.size() >= 3 && !params[2].isNull()) - banTime = params[2].get_int64(); + if (request.params.size() >= 3 && !request.params[2].isNull()) + banTime = request.params[2].get_int64(); bool absolute = false; - if (params.size() == 4 && params[3].isTrue()) + if (request.params.size() == 4 && request.params[3].isTrue()) absolute = true; isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); @@ -520,9 +520,9 @@ UniValue setban(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue listbanned(const UniValue& params, bool fHelp) +UniValue listbanned(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "listbanned\n" "\nList all banned IPs/Subnets.\n" @@ -553,9 +553,9 @@ UniValue listbanned(const UniValue& params, bool fHelp) return bannedAddresses; } -UniValue clearbanned(const UniValue& params, bool fHelp) +UniValue clearbanned(const JSONRPCRequest& request) { - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "clearbanned\n" "\nClear all banned IPs.\n" diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index bb885bb5a6..ec186f4fc5 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -26,13 +26,13 @@ using namespace std; * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html */ -string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) +UniValue JSONRPCRequestObj(const string& strMethod, const UniValue& params, const UniValue& id) { UniValue request(UniValue::VOBJ); request.push_back(Pair("method", strMethod)); request.push_back(Pair("params", params)); request.push_back(Pair("id", id)); - return request.write() + "\n"; + return request; } UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id) diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 1d2ef0e41e..c74fa0070f 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -77,7 +77,7 @@ enum RPCErrorCode RPC_WALLET_ALREADY_UNLOCKED = -17, //!< Wallet is already unlocked }; -std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id); +UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id); UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id); std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); UniValue JSONRPCError(int code, const std::string& message); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index d2ad0a52b7..0656a61755 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -126,9 +126,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) } } -UniValue getrawtransaction(const UniValue& params, bool fHelp) +UniValue getrawtransaction(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getrawtransaction \"txid\" ( verbose )\n" "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" @@ -198,11 +198,11 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) LOCK(cs_main); - uint256 hash = ParseHashV(params[0], "parameter 1"); + uint256 hash = ParseHashV(request.params[0], "parameter 1"); bool fVerbose = false; - if (params.size() > 1) - fVerbose = (params[1].get_int() != 0); + if (request.params.size() > 1) + fVerbose = (request.params[1].get_int() != 0); CTransaction tx; uint256 hashBlock; @@ -220,9 +220,9 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) return result; } -UniValue gettxoutproof(const UniValue& params, bool fHelp) +UniValue gettxoutproof(const JSONRPCRequest& request) { - if (fHelp || (params.size() != 1 && params.size() != 2)) + if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) throw runtime_error( "gettxoutproof [\"txid\",...] ( blockhash )\n" "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" @@ -244,7 +244,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp) set<uint256> setTxids; uint256 oneTxid; - UniValue txids = params[0].get_array(); + UniValue txids = request.params[0].get_array(); for (unsigned int idx = 0; idx < txids.size(); idx++) { const UniValue& txid = txids[idx]; if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) @@ -261,9 +261,9 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp) CBlockIndex* pblockindex = NULL; uint256 hashBlock; - if (params.size() > 1) + if (request.params.size() > 1) { - hashBlock = uint256S(params[1].get_str()); + hashBlock = uint256S(request.params[1].get_str()); if (!mapBlockIndex.count(hashBlock)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); pblockindex = mapBlockIndex[hashBlock]; @@ -301,9 +301,9 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp) return strHex; } -UniValue verifytxoutproof(const UniValue& params, bool fHelp) +UniValue verifytxoutproof(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "verifytxoutproof \"proof\"\n" "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" @@ -314,7 +314,7 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp) "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" ); - CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); + CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); CMerkleBlock merkleBlock; ssMB >> merkleBlock; @@ -335,9 +335,9 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp) return res; } -UniValue createrawtransaction(const UniValue& params, bool fHelp) +UniValue createrawtransaction(const JSONRPCRequest& request) { - if (fHelp || params.size() < 2 || params.size() > 3) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" @@ -373,17 +373,17 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true); - if (params[0].isNull() || params[1].isNull()) + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true); + if (request.params[0].isNull() || request.params[1].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); - UniValue inputs = params[0].get_array(); - UniValue sendTo = params[1].get_obj(); + UniValue inputs = request.params[0].get_array(); + UniValue sendTo = request.params[1].get_obj(); CMutableTransaction rawTx; - if (params.size() > 2 && !params[2].isNull()) { - int64_t nLockTime = params[2].get_int64(); + if (request.params.size() > 2 && !request.params[2].isNull()) { + int64_t nLockTime = request.params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; @@ -448,9 +448,9 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) return EncodeHexTx(rawTx); } -UniValue decoderawtransaction(const UniValue& params, bool fHelp) +UniValue decoderawtransaction(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "decoderawtransaction \"hexstring\"\n" "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" @@ -504,11 +504,11 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); CTransaction tx; - if (!DecodeHexTx(tx, params[0].get_str(), true)) + if (!DecodeHexTx(tx, request.params[0].get_str(), true)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); UniValue result(UniValue::VOBJ); @@ -517,9 +517,9 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) return result; } -UniValue decodescript(const UniValue& params, bool fHelp) +UniValue decodescript(const JSONRPCRequest& request) { - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "decodescript \"hex\"\n" "\nDecode a hex-encoded script.\n" @@ -535,26 +535,34 @@ UniValue decodescript(const UniValue& params, bool fHelp) " \"address\" (string) bitcoin address\n" " ,...\n" " ],\n" - " \"p2sh\",\"address\" (string) script address\n" + " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" "}\n" "\nExamples:\n" + HelpExampleCli("decodescript", "\"hexstring\"") + HelpExampleRpc("decodescript", "\"hexstring\"") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); UniValue r(UniValue::VOBJ); CScript script; - if (params[0].get_str().size() > 0){ - vector<unsigned char> scriptData(ParseHexV(params[0], "argument")); + if (request.params[0].get_str().size() > 0){ + vector<unsigned char> scriptData(ParseHexV(request.params[0], "argument")); script = CScript(scriptData.begin(), scriptData.end()); } else { // Empty scripts are valid } ScriptPubKeyToJSON(script, r, false); - r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + UniValue type; + type = find_value(r, "type"); + + if (type.isStr() && type.get_str() != "scripthash") { + // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, + // don't return the address for a P2SH of the P2SH. + r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + } + return r; } @@ -570,9 +578,9 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std:: vErrorsRet.push_back(entry); } -UniValue signrawtransaction(const UniValue& params, bool fHelp) +UniValue signrawtransaction(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 4) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw runtime_error( "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n" @@ -636,9 +644,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) #else LOCK(cs_main); #endif - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); - vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); + vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); vector<CMutableTransaction> txVariants; while (!ssData.empty()) { @@ -679,9 +687,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) bool fGivenKeys = false; CBasicKeyStore tempKeystore; - if (params.size() > 2 && !params[2].isNull()) { + if (request.params.size() > 2 && !request.params[2].isNull()) { fGivenKeys = true; - UniValue keys = params[2].get_array(); + UniValue keys = request.params[2].get_array(); for (unsigned int idx = 0; idx < keys.size(); idx++) { UniValue k = keys[idx]; CBitcoinSecret vchSecret; @@ -700,8 +708,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) #endif // Add previous txouts given in the RPC call: - if (params.size() > 1 && !params[1].isNull()) { - UniValue prevTxs = params[1].get_array(); + if (request.params.size() > 1 && !request.params[1].isNull()) { + UniValue prevTxs = request.params[1].get_array(); for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { const UniValue& p = prevTxs[idx]; if (!p.isObject()) @@ -769,7 +777,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) #endif int nHashType = SIGHASH_ALL; - if (params.size() > 3 && !params[3].isNull()) { + if (request.params.size() > 3 && !request.params[3].isNull()) { static map<string, int> mapSigHashValues = boost::assign::map_list_of (string("ALL"), int(SIGHASH_ALL)) @@ -779,7 +787,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) (string("SINGLE"), int(SIGHASH_SINGLE)) (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) ; - string strHashType = params[3].get_str(); + string strHashType = request.params[3].get_str(); if (mapSigHashValues.count(strHashType)) nHashType = mapSigHashValues[strHashType]; else @@ -834,9 +842,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) return result; } -UniValue sendrawtransaction(const UniValue& params, bool fHelp) +UniValue sendrawtransaction(const JSONRPCRequest& request) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "sendrawtransaction \"hexstring\" ( allowhighfees )\n" "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" @@ -858,16 +866,17 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); // parse hex string from parameter CTransaction tx; - if (!DecodeHexTx(tx, params[0].get_str())) + if (!DecodeHexTx(tx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); uint256 hashTx = tx.GetHash(); + bool fLimitFree = false; CAmount nMaxRawTxFee = maxTxFee; - if (params.size() > 1 && params[1].get_bool()) + if (request.params.size() > 1 && request.params[1].get_bool()) nMaxRawTxFee = 0; CCoinsViewCache &view = *pcoinsTip; @@ -878,7 +887,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, tx, fLimitFree, &fMissingInputs, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 5fb97f7496..29d0bee1b2 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -195,10 +195,11 @@ std::string CRPCTable::help(const std::string& strCommand) const continue; try { - UniValue params; + JSONRPCRequest jreq; + jreq.fHelp = true; rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) - (*pfn)(params, true); + (*pfn)(jreq); } catch (const std::exception& e) { @@ -228,9 +229,9 @@ std::string CRPCTable::help(const std::string& strCommand) const return strRet; } -UniValue help(const UniValue& params, bool fHelp) +UniValue help(const JSONRPCRequest& jsonRequest) { - if (fHelp || params.size() > 1) + if (jsonRequest.fHelp || jsonRequest.params.size() > 1) throw runtime_error( "help ( \"command\" )\n" "\nList all commands, or get help for a specified command.\n" @@ -241,17 +242,17 @@ UniValue help(const UniValue& params, bool fHelp) ); string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); + if (jsonRequest.params.size() > 0) + strCommand = jsonRequest.params[0].get_str(); return tableRPC.help(strCommand); } -UniValue stop(const UniValue& params, bool fHelp) +UniValue stop(const JSONRPCRequest& jsonRequest) { // Accept the deprecated and ignored 'detach' boolean argument - if (fHelp || params.size() > 1) + if (jsonRequest.fHelp || jsonRequest.params.size() > 1) throw runtime_error( "stop\n" "\nStop Bitcoin server."); @@ -354,7 +355,7 @@ bool RPCIsInWarmup(std::string *outStatus) return fRPCInWarmup; } -void JSONRequest::parse(const UniValue& valRequest) +void JSONRPCRequest::parse(const UniValue& valRequest) { // Parse request if (!valRequest.isObject()) @@ -388,11 +389,11 @@ static UniValue JSONRPCExecOne(const UniValue& req) { UniValue rpc_result(UniValue::VOBJ); - JSONRequest jreq; + JSONRPCRequest jreq; try { jreq.parse(req); - UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + UniValue result = tableRPC.execute(jreq); rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } catch (const UniValue& objError) @@ -417,7 +418,7 @@ std::string JSONRPCExecBatch(const UniValue& vReq) return ret.write() + "\n"; } -UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const +UniValue CRPCTable::execute(const JSONRPCRequest &request) const { // Return immediately if in warmup { @@ -427,7 +428,7 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms } // Find method - const CRPCCommand *pcmd = tableRPC[strMethod]; + const CRPCCommand *pcmd = tableRPC[request.strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); @@ -436,7 +437,7 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms try { // Execute - return pcmd->actor(params, false); + return pcmd->actor(request); } catch (const std::exception& e) { diff --git a/src/rpc/server.h b/src/rpc/server.h index 4e0aa2c6d6..c59886222c 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -41,14 +41,17 @@ struct UniValueType { UniValue::VType type; }; -class JSONRequest +class JSONRPCRequest { public: UniValue id; std::string strMethod; UniValue params; + bool fHelp; + std::string URI; + std::string authUser; - JSONRequest() { id = NullUniValue; } + JSONRPCRequest() { id = NullUniValue; params = NullUniValue; fHelp = false; } void parse(const UniValue& valRequest); }; @@ -122,7 +125,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface); */ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds); -typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp); +typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest); class CRPCCommand { @@ -147,12 +150,11 @@ public: /** * Execute a method. - * @param method Method to execute - * @param params UniValue Array of arguments (JSON objects) + * @param request The JSONRPCRequest to execute * @returns Result of the call. * @throws an exception (UniValue) when an error happens. */ - UniValue execute(const std::string &method, const UniValue ¶ms) const; + UniValue execute(const JSONRPCRequest &request) const; /** * Returns a list of registered commands diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e0f2f89190..0e17ddc130 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -79,8 +79,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { return false; } } else { - // Non-canonical public key: neither compressed nor uncompressed - return false; + // Non-canonical public key: neither compressed nor uncompressed + return false; + } + return true; +} + +bool static IsCompressedPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } + if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) { + // Non-canonical public key: invalid prefix for compressed key + return false; } return true; } @@ -199,10 +211,14 @@ bool CheckSignatureEncoding(const vector<unsigned char> &vchSig, unsigned int fl return true; } -bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { - if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) { +bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) { + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) { return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); } + // Only compressed keys are accepted in segwit + if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE); + } return true; } @@ -879,7 +895,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un scriptCode.FindAndDelete(CScript(vchSig)); } - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { //serror is set return false; } @@ -953,7 +969,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Note how this makes the exact order of pubkey/signature evaluation // distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set. // See the script_(in)valid tests for details. - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { // serror is set return false; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 0adc9482ff..79894c5300 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -102,6 +102,10 @@ enum // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed // SCRIPT_VERIFY_NULLFAIL = (1U << 14), + + // Public keys in segregated witness scripts must be compressed + // + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), }; bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 0bf180341e..7467d23b2d 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -29,13 +29,25 @@ unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore) return nResult; } -isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion) +{ + bool isInvalid = false; + return IsMine(keystore, scriptPubKey, isInvalid, sigversion); +} + +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion) +{ + bool isInvalid = false; + return IsMine(keystore, dest, isInvalid, sigversion); +} + +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion) { CScript script = GetScriptForDestination(dest); - return IsMine(keystore, script); + return IsMine(keystore, script, isInvalid, sigversion); } -isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { vector<valtype> vSolutions; txnouttype whichType; @@ -53,12 +65,35 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); + if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) { + isInvalid = true; + return ISMINE_NO; + } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; - case TX_PUBKEYHASH: case TX_WITNESS_V0_KEYHASH: + { + if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + // We do not support bare witness outputs unless the P2SH version of it would be + // acceptable as well. This protects against matching before segwit activates. + // This also applies to the P2WSH case. + break; + } + isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) + return ret; + break; + } + case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); + if (sigversion != SIGVERSION_BASE) { + CPubKey pubkey; + if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { + isInvalid = true; + return ISMINE_NO; + } + } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; @@ -67,21 +102,24 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) + isminetype ret = IsMine(keystore, subscript, isInvalid); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_WITNESS_V0_SCRIPTHASH: { + if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + break; + } uint160 hash; CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin()); CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) + isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; @@ -95,6 +133,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (sigversion != SIGVERSION_BASE) { + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i].size() != 33) { + isInvalid = true; + return ISMINE_NO; + } + } + } if (HaveKeys(keys, keystore) == keys.size()) return ISMINE_SPENDABLE; break; diff --git a/src/script/ismine.h b/src/script/ismine.h index 4b7db8802b..ec7a620e33 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -28,7 +28,14 @@ enum isminetype /** used for bitflags of isminetype */ typedef uint8_t isminefilter; -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); +/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion + * and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as + * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed + * keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future + */ +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SIGVERSION_BASE); #endif // BITCOIN_SCRIPT_ISMINE_H diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index e27b715c2c..2c5359fe8a 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -85,6 +85,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness requires only-redeemscript scriptSig"; case SCRIPT_ERR_WITNESS_UNEXPECTED: return "Witness provided for non-witness script"; + case SCRIPT_ERR_WITNESS_PUBKEYTYPE: + return "Using non-compressed keys in segwit"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index bccfdb99e2..430836991b 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -62,6 +62,7 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_MALLEATED, SCRIPT_ERR_WITNESS_MALLEATED_P2SH, SCRIPT_ERR_WITNESS_UNEXPECTED, + SCRIPT_ERR_WITNESS_PUBKEYTYPE, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 87f38d9c72..f552ad5bba 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -26,6 +26,10 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, if (!keystore->GetKey(address, key)) return false; + // Signing with uncompressed keys is disabled in witness scripts + if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed()) + return false; + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!key.Sign(hash, vchSig)) return false; diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 5ccaeb57bb..adff09f754 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -26,7 +26,7 @@ public: void MakeDeterministic() { nKey.SetNull(); - seed_insecure_rand(true); + insecure_rand = FastRandomContext(true); } int RandomInt(int nMax) diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index e692326559..b487686136 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "coins.h" -#include "random.h" +#include "test_random.h" #include "script/standard.h" #include "uint256.h" #include "utilstrencodings.h" diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index ff0adae1d2..c7b4fb240c 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -9,7 +9,7 @@ #include "crypto/sha512.h" #include "crypto/hmac_sha256.h" #include "crypto/hmac_sha512.h" -#include "random.h" +#include "test_random.h" #include "utilstrencodings.h" #include "test/test_bitcoin.h" diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 06103ea5bd..5c054ed3e8 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -1855,6 +1855,8 @@ "OK", "P2SH with CLEANSTACK" ], + +["Testing with uncompressed keys in witness v0 without WITNESS_PUBKEYTYPE"], [ [ "304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101", @@ -2139,7 +2141,371 @@ "P2PK with witness" ], -["CHECKSEQUENCEVERIFY tests"], +["Testing with compressed keys in witness v0 with WITNESS_PUBKEYTYPE"], +[ + [ + "304402204256146fcf8e73b0fd817ffa2a4e408ff0418ff987dd08a4f485b62546f6c43c02203f3c8c3e2febc051e1222867f5f9d0eaf039d6792911c10940aa3cc74123378e01", + "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + 0.00000001 + ], + "", + "0 0x20 0x1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2WSH with compressed key" +], +[ + [ + "304402204edf27486f11432466b744df533e1acac727e0c83e5f912eb289a3df5bf8035f022075809fdd876ede40ad21667eba8b7e96394938f9c9c50f11b6a1280cce2cea8601", + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 0.00000001 + ], + "", + "0 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2WPKH with compressed key" +], +[ + [ + "304402203a549090cc46bce1e5e95c4922ea2c12747988e0207b04c42f81cdbe87bb1539022050f57a245b875fd5119c419aaf050bcdf41384f0765f04b809e5bced1fe7093d01", + "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + 0.00000001 + ], + "0x22 0x00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + "HASH160 0x14 0xe4300531190587e3880d4c3004f5355d88ff928d EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2SH(P2WSH) with compressed key" +], +[ + [ + "304402201bc0d53046827f4a35a3166e33e3b3366c4085540dc383b95d21ed2ab11e368a0220333e78c6231214f5f8e59621e15d7eeab0d4e4d0796437e00bfbd2680c5f9c1701", + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 0.00000001 + ], + "0x16 0x0014751e76e8199196d454941c45d1b3a323f1433bd6", + "HASH160 0x14 0xbcfeb728b584253d5f3f70bcb780e9ef218a68f4 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2SH(P2WPKH) with compressed key" +], + +["Testing with uncompressed keys in witness v0 with WITNESS_PUBKEYTYPE"], +[ + [ + "304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000001 + ], + "", + "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2WSH" +], +[ + [ + "304402201e7216e5ccb3b61d46946ec6cc7e8c4e0117d13ac2fd4b152197e4805191c74202203e9903e33e84d9ee1dd13fb057afb7ccfb47006c23f6a067185efbc9dd780fc501", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000001 + ], + "", + "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2WPKH" +], +[ + [ + "3044022066e02c19a513049d49349cf5311a1b012b7c4fae023795a18ab1d91c23496c22022025e216342c8e07ce8ef51e8daee88f84306a9de66236cab230bb63067ded1ad301", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000001 + ], + "0x22 0x0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "HASH160 0x14 0xf386c2ba255cc56d20cfa6ea8b062f8b59945518 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2SH(P2WSH)" +], +[ + [ + "304402200929d11561cd958460371200f82e9cae64c727a495715a31828e27a7ad57b36d0220361732ced04a6f97351ecca21a56d0b8cd4932c1da1f8f569a2b68e5e48aed7801", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000001 + ], + "0x16 0x001491b24bf9f5288532960ac687abb035127b1d28a5", + "HASH160 0x14 0x17743beb429c55c942d2ec703b98c4d57c2df5c6 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2SH(P2WPKH)" +], + +["Testing P2WSH multisig with compressed keys"], +[ + [ + "", + "304402207eb8a59b5c65fc3f6aeef77066556ed5c541948a53a3ba7f7c375b8eed76ee7502201e036a7a9a98ff919ff94dc905d67a1ec006f79ef7cff0708485c8bb79dce38e01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x06c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2WSH CHECKMULTISIG with compressed keys" +], +[ + [ + "", + "3044022033706aed33b8155d5486df3b9bca8cdd3bd4bdb5436dce46d72cdaba51d22b4002203626e94fe53a178af46624f17315c6931f20a30b103f5e044e1eda0c3fe185c601", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x002006c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "HASH160 0x14 0x26282aad7c29369d15fed062a778b6100d31a340 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with compressed keys" +], +[ + [ + "", + "304402204048b7371ab1c544362efb89af0c80154747d665aa4fcfb2edfd2d161e57b42e02207e043748e96637080ffc3acbd4dcc6fee1e58d30f6d1269535f32188e5ddae7301", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x06c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2WSH CHECKMULTISIG with compressed keys" +], +[ + [ + "", + "3044022073902ef0b8a554c36c44cc03c1b64df96ce2914ebcf946f5bb36078fd5245cdf02205b148f1ba127065fb8c83a5a9576f2dcd111739788ed4bb3ee08b2bd3860c91c01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x002006c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "HASH160 0x14 0x26282aad7c29369d15fed062a778b6100d31a340 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with compressed keys" +], + +["Testing P2WSH multisig with compressed and uncompressed keys (first key being the key closer to the top of stack)"], +[ + [ + "", + "304402202d092ededd1f060609dbf8cb76950634ff42b3e62cf4adb69ab92397b07d742302204ff886f8d0817491a96d1daccdcc820f6feb122ee6230143303100db37dfa79f01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402202dd7e91243f2235481ffb626c3b7baf2c859ae3a5a77fb750ef97b99a8125dc002204960de3d3c3ab9496e218ec57e5240e0e10a6f9546316fe240c216d45116d29301", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402202d092ededd1f060609dbf8cb76950634ff42b3e62cf4adb69ab92397b07d742302204ff886f8d0817491a96d1daccdcc820f6feb122ee6230143303100db37dfa79f01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402202dd7e91243f2235481ffb626c3b7baf2c859ae3a5a77fb750ef97b99a8125dc002204960de3d3c3ab9496e218ec57e5240e0e10a6f9546316fe240c216d45116d29301", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402201e9e6f7deef5b2f21d8223c5189b7d5e82d237c10e97165dd08f547c4e5ce6ed02206796372eb1cc6acb52e13ee2d7f45807780bf96b132cb6697f69434be74b1af901", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "3044022045e667f3f0f3147b95597a24babe9afecea1f649fd23637dfa7ed7e9f3ac18440220295748e81005231135289fe3a88338dabba55afa1bdb4478691337009d82b68d01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "304402201e9e6f7deef5b2f21d8223c5189b7d5e82d237c10e97165dd08f547c4e5ce6ed02206796372eb1cc6acb52e13ee2d7f45807780bf96b132cb6697f69434be74b1af901", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "3044022045e667f3f0f3147b95597a24babe9afecea1f649fd23637dfa7ed7e9f3ac18440220295748e81005231135289fe3a88338dabba55afa1bdb4478691337009d82b68d01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "3044022046f5367a261fd8f8d7de6eb390491344f8ec2501638fb9a1095a0599a21d3f4c02205c1b3b51d20091c5f1020841bbca87b44ebe25405c64e4acf758f2eae8665f8401", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key" +], +[ + [ + "", + "3044022053e210e4fb1881e6092fd75c3efc5163105599e246ded661c0ee2b5682cc2d6c02203a26b7ada8682a095b84c6d1b881637000b47d761fc837c4cee33555296d63f101", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key" +], +[ + [ + "", + "3044022046f5367a261fd8f8d7de6eb390491344f8ec2501638fb9a1095a0599a21d3f4c02205c1b3b51d20091c5f1020841bbca87b44ebe25405c64e4acf758f2eae8665f8401", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used" +], +[ + [ + "", + "3044022053e210e4fb1881e6092fd75c3efc5163105599e246ded661c0ee2b5682cc2d6c02203a26b7ada8682a095b84c6d1b881637000b47d761fc837c4cee33555296d63f101", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used" +], +[ + [ + "", + "304402206c6d9f5daf85b54af2a93ec38b15ab27f205dbf5c735365ff12451e43613d1f40220736a44be63423ed5ebf53491618b7cc3d8a5093861908da853739c73717938b701", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key" +], +[ + [ + "", + "30440220687871bc6144012d75baf585bb26ce13997f7d8c626f4d8825b069c3b2d064470220108936fe1c57327764782253e99090b09c203ec400ed35ce9e026ce2ecf842a001", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key" +], +[ + [ + "", + "304402206c6d9f5daf85b54af2a93ec38b15ab27f205dbf5c735365ff12451e43613d1f40220736a44be63423ed5ebf53491618b7cc3d8a5093861908da853739c73717938b701", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key" +], +[ + [ + "", + "30440220687871bc6144012d75baf585bb26ce13997f7d8c626f4d8825b069c3b2d064470220108936fe1c57327764782253e99090b09c203ec400ed35ce9e026ce2ecf842a001", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key" +], + +["CHECKSEQUENCEVERIFY tests"], ["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on a empty stack"], ["-1", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "NEGATIVE_LOCKTIME", "CSV automatically fails if stack top is negative"], ["0x0100", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY,MINIMALDATA", "UNKNOWN_ERROR", "CSV fails if stack top is not minimally encoded"], diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index b40ab848dc..66ca381ea7 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -4,7 +4,7 @@ #include "consensus/merkle.h" #include "test/test_bitcoin.h" -#include "random.h" +#include "test_random.h" #include <boost/test/unit_test.hpp> diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index d3aa2364d1..d6d7b5716e 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -18,6 +18,8 @@ #include "test/test_bitcoin.h" +#include <memory> + #include <boost/test/unit_test.hpp> BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) @@ -105,7 +107,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, uint256 hashHighFeeTx = tx.GetHash(); mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[1].GetHash() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[2].GetHash() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3].GetHash() == hashMediumFeeTx); @@ -184,7 +186,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // Note that by default, these tests run with size accounting enabled. const CChainParams& chainparams = Params(CBaseChainParams::MAIN); CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; - CBlockTemplate *pblocktemplate; + std::unique_ptr<CBlockTemplate> pblocktemplate; CMutableTransaction tx,tx2; CScript script; uint256 hash; @@ -222,15 +224,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->nNonce = blockinfo[i].nonce; CValidationState state; - BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, connman)); + BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)); BOOST_CHECK(state.IsValid()); pblock->hashPrevBlock = pblock->GetHash(); } - delete pblocktemplate; // Just to make sure we can still make simple blocks BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); - delete pblocktemplate; const CAmount BLOCKSUBSIDY = 50*COIN; const CAmount LOWFEE = CENT; @@ -269,7 +269,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); - delete pblocktemplate; mempool.clear(); // block size > limit @@ -290,7 +289,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); - delete pblocktemplate; mempool.clear(); // orphan in mempool, template creation fails @@ -314,7 +312,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); - delete pblocktemplate; mempool.clear(); // coinbase in mempool, template creation fails @@ -372,7 +369,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.SetTip(next); } BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); - delete pblocktemplate; // Extend to a 210000-long block chain. while (chainActive.Tip()->nHeight < 210000) { CBlockIndex* prev = chainActive.Tip(); @@ -385,7 +381,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.SetTip(next); } BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); - delete pblocktemplate; // Delete the dummy blocks again. while (chainActive.Tip()->nHeight > nHeight) { CBlockIndex* del = chainActive.Tip(); @@ -478,7 +473,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // but relative locked txs will if inconsistently added to mempool. // For now these will still generate a valid template until BIP68 soft fork BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3); - delete pblocktemplate; // However if we advance height by 1 and time by 512, all of them should be mined for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast @@ -487,7 +481,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5); - delete pblocktemplate; chainActive.Tip()->nHeight--; SetMockTime(0); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 4a7d6e778f..f4b5768693 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -23,7 +23,7 @@ public: void MakeDeterministic() { nKey.SetNull(); - seed_insecure_rand(true); + insecure_rand = FastRandomContext(true); } }; diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index e9c8691745..b7f83d38f0 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -9,7 +9,7 @@ #include "uint256.h" #include "arith_uint256.h" #include "version.h" -#include "random.h" +#include "test_random.h" #include "test/test_bitcoin.h" #include <vector> diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index b8c45ca564..6cad02e738 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -4,7 +4,7 @@ #include <vector> #include "prevector.h" -#include "random.h" +#include "test_random.h" #include "serialize.h" #include "streams.h" @@ -27,8 +27,7 @@ class prevector_tester { typedef typename pretype::size_type Size; bool passed = true; - uint32_t insecure_rand_Rz_cache; - uint32_t insecure_rand_Rw_cache; + FastRandomContext rand_cache; template <typename A, typename B> @@ -171,15 +170,14 @@ public: test(); } ~prevector_tester() { - BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: " - << insecure_rand_Rz_cache + BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: " + << rand_cache.Rz << ", insecure_rand_Rw: " - << insecure_rand_Rw_cache); + << rand_cache.Rw); } prevector_tester() { seed_insecure_rand(); - insecure_rand_Rz_cache = insecure_rand_Rz; - insecure_rand_Rw_cache = insecure_rand_Rw; + rand_cache = insecure_rand_ctx; } }; diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index a15915aad2..a3d1a25589 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -24,11 +24,14 @@ UniValue CallRPC(string args) boost::split(vArgs, args, boost::is_any_of(" \t")); string strMethod = vArgs[0]; vArgs.erase(vArgs.begin()); - UniValue params = RPCConvertValues(strMethod, vArgs); + JSONRPCRequest request; + request.strMethod = strMethod; + request.params = RPCConvertValues(strMethod, vArgs); + request.fHelp = false; BOOST_CHECK(tableRPC[strMethod]); rpcfn_type method = tableRPC[strMethod]->actor; try { - UniValue result = (*method)(params, false); + UniValue result = (*method)(request); return result; } catch (const UniValue& objError) { diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index aa12dfbd54..891ecf5015 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -42,8 +42,6 @@ static void MicroSleep(uint64_t n) BOOST_AUTO_TEST_CASE(manythreads) { - seed_insecure_rand(false); - // Stress test: hundreds of microsecond-scheduled tasks, // serviced by 10 threads. // @@ -58,7 +56,7 @@ BOOST_AUTO_TEST_CASE(manythreads) boost::mutex counterMutex[10]; int counter[10] = { 0 }; - boost::random::mt19937 rng(insecure_rand()); + boost::random::mt19937 rng(42); boost::random::uniform_int_distribution<> zeroToNine(0, 9); boost::random::uniform_int_distribution<> randomMsec(-11, 1000); boost::random::uniform_int_distribution<> randomDelta(-1000, 1000); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 7971b5122f..561adb8ea2 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -99,6 +99,7 @@ static ScriptErrorDesc script_errors[]={ {SCRIPT_ERR_WITNESS_MALLEATED, "WITNESS_MALLEATED"}, {SCRIPT_ERR_WITNESS_MALLEATED_P2SH, "WITNESS_MALLEATED_P2SH"}, {SCRIPT_ERR_WITNESS_UNEXPECTED, "WITNESS_UNEXPECTED"}, + {SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"}, }; const char *FormatScriptError(ScriptError_t err) @@ -825,6 +826,99 @@ BOOST_AUTO_TEST_CASE(script_build) "P2PK with witness", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH ).PushSig(keys.key0).Push("0").AsWit().ScriptError(SCRIPT_ERR_WITNESS_UNEXPECTED)); + // Compressed keys should pass SCRIPT_VERIFY_WITNESS_PUBKEYTYPE + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG, + "Basic P2WSH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).PushWitSig(keys.key0C).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C), + "Basic P2WPKH with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_PKH, + 0, 1).PushWitSig(keys.key0C).Push(keys.pubkey0C).AsWit()); + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG, + "Basic P2SH(P2WSH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).PushWitSig(keys.key0C).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C), + "Basic P2SH(P2WPKH) with compressed key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_PKH, + 0, 1).PushWitSig(keys.key0C).Push(keys.pubkey0C).AsWit().PushRedeem()); + + // Testing uncompressed key in witness with SCRIPT_VERIFY_WITNESS_PUBKEYTYPE + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG, + "Basic P2WSH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0), + "Basic P2WPKH", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_PKH, + 0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0) << OP_CHECKSIG, + "Basic P2SH(P2WSH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0), + "Basic P2SH(P2WPKH)", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_PKH, + 0, 1).PushWitSig(keys.key0).Push(keys.pubkey0).AsWit().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + + // P2WSH 1-of-2 multisig with compressed keys + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with compressed keys", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem()); + + // P2WSH 1-of-2 multisig with first key uncompressed + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1C) << ToByteVector(keys.pubkey0) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1C).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + // P2WSH 1-of-2 multisig with second key uncompressed + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key0C).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().PushRedeem()); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, false, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + tests.push_back(TestBuilder(CScript() << OP_1 << ToByteVector(keys.pubkey1) << ToByteVector(keys.pubkey0C) << OP_2 << OP_CHECKMULTISIG, + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key", SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, true, WITNESS_SH, + 0, 1).Push(CScript()).AsWit().PushWitSig(keys.key1).PushWitRedeem().PushRedeem().ScriptError(SCRIPT_ERR_WITNESS_PUBKEYTYPE)); + std::set<std::string> tests_set; { diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 4a48347b70..0b1050d020 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -6,7 +6,7 @@ #include "data/sighash.json.h" #include "hash.h" #include "main.h" // For CheckTransaction -#include "random.h" +#include "test_random.h" #include "script/interpreter.h" #include "script/script.h" #include "serialize.h" diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index f14b902fe1..b19f8fbffb 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chain.h" -#include "random.h" +#include "test_random.h" #include "util.h" #include "test/test_bitcoin.h" diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 02843d8525..98f4ed939f 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -22,11 +22,14 @@ #include "test/testutil.h" +#include <memory> + #include <boost/filesystem.hpp> #include <boost/test/unit_test.hpp> #include <boost/thread.hpp> std::unique_ptr<CConnman> g_connman; +FastRandomContext insecure_rand_ctx(true); extern bool fPrintToConsole; extern void noui_connect(); @@ -110,7 +113,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); - CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); CBlock& block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: @@ -124,10 +127,9 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; CValidationState state; - ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, connman); + ProcessNewBlock(state, chainparams, NULL, &block, true, NULL); CBlock result = block; - delete pblocktemplate; return result; } diff --git a/src/test/test_random.h b/src/test/test_random.h new file mode 100644 index 0000000000..e61b92b7bc --- /dev/null +++ b/src/test/test_random.h @@ -0,0 +1,23 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_RANDOM_H +#define BITCOIN_TEST_RANDOM_H + +#include "random.h" + +extern FastRandomContext insecure_rand_ctx; + +static inline void seed_insecure_rand(bool fDeterministic = false) +{ + insecure_rand_ctx = FastRandomContext(fDeterministic); +} + +static inline uint32_t insecure_rand(void) +{ + return insecure_rand_ctx.rand32(); +} + +#endif diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 6163d2f630..34d9547f3d 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2015 The Bitcoin Core developers +// Copyright (c) 2011-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. @@ -55,7 +55,8 @@ static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of (string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY) (string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) (string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS) - (string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); + (string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) + (string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE); unsigned int ParseScriptFlags(string strFlags) { @@ -429,7 +430,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { mtx.nVersion = 1; CKey key; - key.MakeNewKey(false); + key.MakeNewKey(true); // Need to use compressed keys in segwit or the signing will fail CBasicKeyStore keystore; keystore.AddKeyPubKey(key, key.GetPubKey()); CKeyID hash = key.GetPubKey().GetID(); @@ -625,30 +626,13 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); - // Witness pay-to-uncompressed-pubkey (v1). - CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1); - CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2); - CheckWithFlag(output1, input1, 0, true); - CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); - CheckWithFlag(output1, input2, 0, true); - CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + // Signing disabled for witness pay-to-uncompressed-pubkey (v1). + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1, false); + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2, false); - // P2SH witness pay-to-uncompressed-pubkey (v1). - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1); - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2); - ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1L)); - CheckWithFlag(output1, input1, 0, true); - CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); - CheckWithFlag(output1, input2, 0, true); - CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); - CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); - CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + // Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1). + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1, false); + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2, false); // Normal 2-of-2 multisig CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index efd4498747..0f1c7ab222 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -6,7 +6,7 @@ #include "clientversion.h" #include "primitives/transaction.h" -#include "random.h" +#include "test_random.h" #include "sync.h" #include "utilstrencodings.h" #include "utilmoneystr.h" diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 1f86a06a3f..784e796998 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -1,9 +1,9 @@ -// 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. #include "chain.h" -#include "random.h" +#include "test_random.h" #include "versionbits.h" #include "test/test_bitcoin.h" #include "chainparams.h" @@ -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 15fa6fbca3..0f1c166abc 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -647,7 +647,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const if (nCheckFrequency == 0) return; - if (insecure_rand() >= nCheckFrequency) + if (GetRand(std::numeric_limits<uint32_t>::max()) >= nCheckFrequency) return; LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); diff --git a/src/txmempool.h b/src/txmempool.h index 941644b2b2..1763930ba0 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -15,6 +15,7 @@ #include "indirectmap.h" #include "primitives/transaction.h" #include "sync.h" +#include "random.h" #undef foreach #include "boost/multi_index_container.hpp" diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 6ddf37658d..085c336ccf 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -13,7 +13,7 @@ CMainSignals& GetMainSignals() } void RegisterValidationInterface(CValidationInterface* pwalletIn) { - g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); + g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); @@ -33,7 +33,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); - g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); + g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); } void UnregisterAllValidationInterfaces() { @@ -47,7 +47,3 @@ void UnregisterAllValidationInterfaces() { g_signals.SyncTransaction.disconnect_all_slots(); g_signals.UpdatedBlockTip.disconnect_all_slots(); } - -void SyncWithWallets(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) { - g_signals.SyncTransaction(tx, pindex, posInBlock); -} diff --git a/src/validationinterface.h b/src/validationinterface.h index 0c91ec8308..a29859999b 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -28,12 +28,10 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn); void UnregisterValidationInterface(CValidationInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllValidationInterfaces(); -/** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock = -1); class CValidationInterface { protected: - virtual void UpdatedBlockTip(const CBlockIndex *pindex) {} + virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {} virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {} virtual void SetBestChain(const CBlockLocator &locator) {} virtual void UpdatedTransaction(const uint256 &hash) {} @@ -49,7 +47,9 @@ protected: struct CMainSignals { /** Notifies listeners of updated block chain tip */ - boost::signals2::signal<void (const CBlockIndex *)> UpdatedBlockTip; + boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip; + /** A posInBlock value for SyncTransaction which indicates the transaction was conflicted, disconnected, or not in a block */ + static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1; /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ 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/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index e80fa7dff8..8a1bbd5684 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -74,12 +74,12 @@ std::string DecodeDumpString(const std::string &str) { return ret.str(); } -UniValue importprivkey(const UniValue& params, bool fHelp) +UniValue importprivkey(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 3) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw runtime_error( "importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n" "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" @@ -104,15 +104,15 @@ UniValue importprivkey(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - string strSecret = params[0].get_str(); + string strSecret = request.params[0].get_str(); string strLabel = ""; - if (params.size() > 1) - strLabel = params[1].get_str(); + if (request.params.size() > 1) + strLabel = request.params[1].get_str(); // Whether to perform rescan after import bool fRescan = true; - if (params.size() > 2) - fRescan = params[2].get_bool(); + if (request.params.size() > 2) + fRescan = request.params[2].get_bool(); if (fRescan && fPruneMode) throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); @@ -184,12 +184,12 @@ void ImportAddress(const CBitcoinAddress& address, const string& strLabel) pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); } -UniValue importaddress(const UniValue& params, bool fHelp) +UniValue importaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 4) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw runtime_error( "importaddress \"address\" ( \"label\" rescan p2sh )\n" "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n" @@ -213,31 +213,31 @@ UniValue importaddress(const UniValue& params, bool fHelp) string strLabel = ""; - if (params.size() > 1) - strLabel = params[1].get_str(); + if (request.params.size() > 1) + strLabel = request.params[1].get_str(); // Whether to perform rescan after import bool fRescan = true; - if (params.size() > 2) - fRescan = params[2].get_bool(); + if (request.params.size() > 2) + fRescan = request.params[2].get_bool(); if (fRescan && fPruneMode) throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); // Whether to import a p2sh version, too bool fP2SH = false; - if (params.size() > 3) - fP2SH = params[3].get_bool(); + if (request.params.size() > 3) + fP2SH = request.params[3].get_bool(); LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); + CBitcoinAddress address(request.params[0].get_str()); if (address.IsValid()) { if (fP2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); ImportAddress(address, strLabel); - } else if (IsHex(params[0].get_str())) { - std::vector<unsigned char> data(ParseHex(params[0].get_str())); + } else if (IsHex(request.params[0].get_str())) { + std::vector<unsigned char> data(ParseHex(request.params[0].get_str())); ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); @@ -252,12 +252,12 @@ UniValue importaddress(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue importprunedfunds(const UniValue& params, bool fHelp) +UniValue importprunedfunds(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 2) + if (request.fHelp || request.params.size() != 2) throw runtime_error( "importprunedfunds\n" "\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n" @@ -267,12 +267,12 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp) ); CTransaction tx; - if (!DecodeHexTx(tx, params[0].get_str())) + if (!DecodeHexTx(tx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); uint256 hashTx = tx.GetHash(); CWalletTx wtx(pwalletMain,tx); - CDataStream ssMB(ParseHexV(params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock merkleBlock; ssMB >> merkleBlock; @@ -311,12 +311,12 @@ UniValue importprunedfunds(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction"); } -UniValue removeprunedfunds(const UniValue& params, bool fHelp) +UniValue removeprunedfunds(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "removeprunedfunds \"txid\"\n" "\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will effect wallet balances.\n" @@ -331,7 +331,7 @@ UniValue removeprunedfunds(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); uint256 hash; - hash.SetHex(params[0].get_str()); + hash.SetHex(request.params[0].get_str()); vector<uint256> vHash; vHash.push_back(hash); vector<uint256> vHashOut; @@ -347,12 +347,12 @@ UniValue removeprunedfunds(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue importpubkey(const UniValue& params, bool fHelp) +UniValue importpubkey(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 4) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw runtime_error( "importpubkey \"pubkey\" ( \"label\" rescan )\n" "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" @@ -372,20 +372,20 @@ UniValue importpubkey(const UniValue& params, bool fHelp) string strLabel = ""; - if (params.size() > 1) - strLabel = params[1].get_str(); + if (request.params.size() > 1) + strLabel = request.params[1].get_str(); // Whether to perform rescan after import bool fRescan = true; - if (params.size() > 2) - fRescan = params[2].get_bool(); + if (request.params.size() > 2) + fRescan = request.params[2].get_bool(); if (fRescan && fPruneMode) throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); - if (!IsHex(params[0].get_str())) + if (!IsHex(request.params[0].get_str())) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); - std::vector<unsigned char> data(ParseHex(params[0].get_str())); + std::vector<unsigned char> data(ParseHex(request.params[0].get_str())); CPubKey pubKey(data.begin(), data.end()); if (!pubKey.IsFullyValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); @@ -405,12 +405,12 @@ UniValue importpubkey(const UniValue& params, bool fHelp) } -UniValue importwallet(const UniValue& params, bool fHelp) +UniValue importwallet(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "importwallet \"filename\"\n" "\nImports keys from a wallet dump file (see dumpwallet).\n" @@ -433,7 +433,7 @@ UniValue importwallet(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); ifstream file; - file.open(params[0].get_str().c_str(), std::ios::in | std::ios::ate); + file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -512,12 +512,12 @@ UniValue importwallet(const UniValue& params, bool fHelp) return NullUniValue; } -UniValue dumpprivkey(const UniValue& params, bool fHelp) +UniValue dumpprivkey(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "dumpprivkey \"bitcoinaddress\"\n" "\nReveals the private key corresponding to 'bitcoinaddress'.\n" @@ -536,7 +536,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - string strAddress = params[0].get_str(); + string strAddress = request.params[0].get_str(); CBitcoinAddress address; if (!address.SetString(strAddress)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); @@ -550,12 +550,12 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp) } -UniValue dumpwallet(const UniValue& params, bool fHelp) +UniValue dumpwallet(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "dumpwallet \"filename\"\n" "\nDumps all wallet keys in a human-readable format.\n" @@ -571,7 +571,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); ofstream file; - file.open(params[0].get_str().c_str()); + file.open(request.params[0].get_str().c_str()); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3eb7e5d9ba..33620aa6ff 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -101,12 +101,12 @@ string AccountFromValue(const UniValue& value) return strAccount; } -UniValue getnewaddress(const UniValue& params, bool fHelp) +UniValue getnewaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( "getnewaddress ( \"account\" )\n" "\nReturns a new Bitcoin address for receiving payments.\n" @@ -125,8 +125,8 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) // Parse the account first so we don't generate a key if there's an error string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); + if (request.params.size() > 0) + strAccount = AccountFromValue(request.params[0]); if (!pwalletMain->IsLocked()) pwalletMain->TopUpKeyPool(); @@ -153,12 +153,12 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) return CBitcoinAddress(pubKey.GetID()); } -UniValue getaccountaddress(const UniValue& params, bool fHelp) +UniValue getaccountaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "getaccountaddress \"account\"\n" "\nDEPRECATED. Returns the current Bitcoin address for receiving payments to this account.\n" @@ -176,7 +176,7 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Parse the account first so we don't generate a key if there's an error - string strAccount = AccountFromValue(params[0]); + string strAccount = AccountFromValue(request.params[0]); UniValue ret(UniValue::VSTR); @@ -185,12 +185,12 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp) } -UniValue getrawchangeaddress(const UniValue& params, bool fHelp) +UniValue getrawchangeaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( "getrawchangeaddress\n" "\nReturns a new Bitcoin address, for receiving change.\n" @@ -220,12 +220,12 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) } -UniValue setaccount(const UniValue& params, bool fHelp) +UniValue setaccount(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "setaccount \"bitcoinaddress\" \"account\"\n" "\nDEPRECATED. Sets the account associated with the given address.\n" @@ -239,13 +239,13 @@ UniValue setaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); + CBitcoinAddress address(request.params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); + if (request.params.size() > 1) + strAccount = AccountFromValue(request.params[1]); // Only add the account if the address is yours. if (IsMine(*pwalletMain, address.Get())) @@ -266,12 +266,12 @@ UniValue setaccount(const UniValue& params, bool fHelp) } -UniValue getaccount(const UniValue& params, bool fHelp) +UniValue getaccount(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "getaccount \"bitcoinaddress\"\n" "\nDEPRECATED. Returns the account associated with the given address.\n" @@ -286,7 +286,7 @@ UniValue getaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); + CBitcoinAddress address(request.params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); @@ -298,12 +298,12 @@ UniValue getaccount(const UniValue& params, bool fHelp) } -UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) +UniValue getaddressesbyaccount(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "getaddressesbyaccount \"account\"\n" "\nDEPRECATED. Returns the list of addresses for the given account.\n" @@ -321,7 +321,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - string strAccount = AccountFromValue(params[0]); + string strAccount = AccountFromValue(request.params[0]); // Find all addresses that have the given account UniValue ret(UniValue::VARR); @@ -369,12 +369,12 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr 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 UniValue& params, bool fHelp) +UniValue sendtoaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 5) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) throw runtime_error( "sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" "\nSend an amount to a given address.\n" @@ -400,25 +400,25 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - CBitcoinAddress address(params[0].get_str()); + CBitcoinAddress address(request.params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); // Amount - CAmount nAmount = AmountFromValue(params[1]); + CAmount nAmount = AmountFromValue(request.params[1]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); // Wallet comments CWalletTx wtx; - if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) - wtx.mapValue["comment"] = params[2].get_str(); - if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) - wtx.mapValue["to"] = params[3].get_str(); + if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) + wtx.mapValue["comment"] = request.params[2].get_str(); + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["to"] = request.params[3].get_str(); bool fSubtractFeeFromAmount = false; - if (params.size() > 4) - fSubtractFeeFromAmount = params[4].get_bool(); + if (request.params.size() > 4) + fSubtractFeeFromAmount = request.params[4].get_bool(); EnsureWalletIsUnlocked(); @@ -427,12 +427,12 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) return wtx.GetHash().GetHex(); } -UniValue listaddressgroupings(const UniValue& params, bool fHelp) +UniValue listaddressgroupings(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp) + if (request.fHelp) throw runtime_error( "listaddressgroupings\n" "\nLists groups of addresses which have had their common ownership\n" @@ -478,12 +478,12 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp) return jsonGroupings; } -UniValue signmessage(const UniValue& params, bool fHelp) +UniValue signmessage(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 2) + if (request.fHelp || request.params.size() != 2) throw runtime_error( "signmessage \"bitcoinaddress\" \"message\"\n" "\nSign a message with the private key of an address" @@ -508,8 +508,8 @@ UniValue signmessage(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); - string strAddress = params[0].get_str(); - string strMessage = params[1].get_str(); + string strAddress = request.params[0].get_str(); + string strMessage = request.params[1].get_str(); CBitcoinAddress addr(strAddress); if (!addr.IsValid()) @@ -534,12 +534,12 @@ UniValue signmessage(const UniValue& params, bool fHelp) return EncodeBase64(&vchSig[0], vchSig.size()); } -UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) +UniValue getreceivedbyaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n" "\nReturns the total amount received by the given bitcoinaddress in transactions with at least minconf confirmations.\n" @@ -562,7 +562,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); + CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); CScript scriptPubKey = GetScriptForDestination(address.Get()); @@ -571,8 +571,8 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) // Minimum confirmations int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); // Tally CAmount nAmount = 0; @@ -592,12 +592,12 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) } -UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) +UniValue getreceivedbyaccount(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "getreceivedbyaccount \"account\" ( minconf )\n" "\nDEPRECATED. Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n" @@ -621,11 +621,11 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) // Minimum confirmations int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); // Get the set of pub keys assigned to account - string strAccount = AccountFromValue(params[0]); + string strAccount = AccountFromValue(request.params[0]); set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount); // Tally @@ -649,12 +649,12 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) } -UniValue getbalance(const UniValue& params, bool fHelp) +UniValue getbalance(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 3) + if (request.fHelp || request.params.size() > 3) throw runtime_error( "getbalance ( \"account\" minconf includeWatchonly )\n" "\nIf account is not specified, returns the server's total available balance.\n" @@ -678,18 +678,18 @@ UniValue getbalance(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (params.size() == 0) + if (request.params.size() == 0) return ValueFromAmount(pwalletMain->GetBalance()); int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].get_int(); + if (request.params.size() > 1) + nMinDepth = request.params[1].get_int(); isminefilter filter = ISMINE_SPENDABLE; - if(params.size() > 2) - if(params[2].get_bool()) + if(request.params.size() > 2) + if(request.params[2].get_bool()) filter = filter | ISMINE_WATCH_ONLY; - if (params[0].get_str() == "*") { + if (request.params[0].get_str() == "*") { // Calculate total balance a different way from GetBalance() // (GetBalance() sums up all unspent TxOuts) // getbalance and "getbalance * 1 true" should return the same number @@ -717,19 +717,19 @@ UniValue getbalance(const UniValue& params, bool fHelp) return ValueFromAmount(nBalance); } - string strAccount = AccountFromValue(params[0]); + string strAccount = AccountFromValue(request.params[0]); CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter); return ValueFromAmount(nBalance); } -UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) +UniValue getunconfirmedbalance(const JSONRPCRequest &request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 0) + if (request.fHelp || request.params.size() > 0) throw runtime_error( "getunconfirmedbalance\n" "Returns the server's total unconfirmed balance\n"); @@ -740,12 +740,12 @@ UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) } -UniValue movecmd(const UniValue& params, bool fHelp) +UniValue movecmd(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 3 || params.size() > 5) + if (request.fHelp || request.params.size() < 3 || request.params.size() > 5) throw runtime_error( "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" @@ -768,17 +768,17 @@ UniValue movecmd(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - CAmount nAmount = AmountFromValue(params[2]); + string strFrom = AccountFromValue(request.params[0]); + string strTo = AccountFromValue(request.params[1]); + CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - if (params.size() > 3) + if (request.params.size() > 3) // unused parameter, used to be nMinDepth, keep type-checking it though - (void)params[3].get_int(); + (void)request.params[3].get_int(); string strComment; - if (params.size() > 4) - strComment = params[4].get_str(); + if (request.params.size() > 4) + strComment = request.params[4].get_str(); if (!pwalletMain->AccountMove(strFrom, strTo, nAmount, strComment)) throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); @@ -787,12 +787,12 @@ UniValue movecmd(const UniValue& params, bool fHelp) } -UniValue sendfrom(const UniValue& params, bool fHelp) +UniValue sendfrom(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 3 || params.size() > 6) + if (request.fHelp || request.params.size() < 3 || request.params.size() > 6) throw runtime_error( "sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a bitcoin address." @@ -820,23 +820,23 @@ UniValue sendfrom(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - string strAccount = AccountFromValue(params[0]); - CBitcoinAddress address(params[1].get_str()); + string strAccount = AccountFromValue(request.params[0]); + CBitcoinAddress address(request.params[1].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CAmount nAmount = AmountFromValue(params[2]); + CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].get_int(); + if (request.params.size() > 3) + nMinDepth = request.params[3].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); + if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty()) + wtx.mapValue["comment"] = request.params[4].get_str(); + if (request.params.size() > 5 && !request.params[5].isNull() && !request.params[5].get_str().empty()) + wtx.mapValue["to"] = request.params[5].get_str(); EnsureWalletIsUnlocked(); @@ -851,12 +851,12 @@ UniValue sendfrom(const UniValue& params, bool fHelp) } -UniValue sendmany(const UniValue& params, bool fHelp) +UniValue sendmany(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 5) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) throw runtime_error( "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" "\nSend multiple times. Amounts are double-precision floating point numbers." @@ -897,20 +897,20 @@ UniValue sendmany(const UniValue& params, bool fHelp) if (pwalletMain->GetBroadcastTransactions() && !g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - string strAccount = AccountFromValue(params[0]); - UniValue sendTo = params[1].get_obj(); + string strAccount = AccountFromValue(request.params[0]); + UniValue sendTo = request.params[1].get_obj(); int nMinDepth = 1; - if (params.size() > 2) - nMinDepth = params[2].get_int(); + if (request.params.size() > 2) + nMinDepth = request.params[2].get_int(); CWalletTx wtx; wtx.strFromAccount = strAccount; - if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) - wtx.mapValue["comment"] = params[3].get_str(); + if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) + wtx.mapValue["comment"] = request.params[3].get_str(); UniValue subtractFeeFromAmount(UniValue::VARR); - if (params.size() > 4) - subtractFeeFromAmount = params[4].get_array(); + if (request.params.size() > 4) + subtractFeeFromAmount = request.params[4].get_array(); set<CBitcoinAddress> setAddress; vector<CRecipient> vecSend; @@ -968,12 +968,12 @@ UniValue sendmany(const UniValue& params, bool fHelp) // Defined in rpc/misc.cpp extern CScript _createmultisig_redeemScript(const UniValue& params); -UniValue addmultisigaddress(const UniValue& params, bool fHelp) +UniValue addmultisigaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 2 || params.size() > 3) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" @@ -1004,11 +1004,11 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); + if (request.params.size() > 2) + strAccount = AccountFromValue(request.params[2]); // Construct using pay-to-script-hash: - CScript inner = _createmultisig_redeemScript(params); + CScript inner = _createmultisig_redeemScript(request.params); CScriptID innerID(inner); pwalletMain->AddCScript(inner); @@ -1025,9 +1025,12 @@ public: bool operator()(const CKeyID &keyID) { CPubKey pubkey; - if (pwalletMain && pwalletMain->GetPubKey(keyID, pubkey)) { - CScript basescript; - basescript << ToByteVector(pubkey) << OP_CHECKSIG; + if (pwalletMain) { + CScript basescript = GetScriptForDestination(keyID); + isminetype typ; + typ = IsMine(*pwalletMain, basescript, SIGVERSION_WITNESS_V0); + if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE) + return false; CScript witscript = GetScriptForWitness(basescript); pwalletMain->AddCScript(witscript); result = CScriptID(witscript); @@ -1045,6 +1048,10 @@ public: result = scriptID; return true; } + isminetype typ; + typ = IsMine(*pwalletMain, subscript, SIGVERSION_WITNESS_V0); + if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE) + return false; CScript witscript = GetScriptForWitness(subscript); pwalletMain->AddCScript(witscript); result = CScriptID(witscript); @@ -1054,12 +1061,12 @@ public: } }; -UniValue addwitnessaddress(const UniValue& params, bool fHelp) +UniValue addwitnessaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) { string msg = "addwitnessaddress \"address\"\n" "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" @@ -1082,7 +1089,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp) } } - CBitcoinAddress address(params[0].get_str()); + CBitcoinAddress address(request.params[0].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); @@ -1090,7 +1097,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp) CTxDestination dest = address.Get(); bool ret = boost::apply_visitor(w, dest); if (!ret) { - throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet"); + throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); } pwalletMain->SetAddressBook(w.result, "", "receive"); @@ -1232,12 +1239,12 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) return ret; } -UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) +UniValue listreceivedbyaddress(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 3) + if (request.fHelp || request.params.size() > 3) throw runtime_error( "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" "\nList balances by receiving address.\n" @@ -1267,15 +1274,15 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - return ListReceived(params, false); + return ListReceived(request.params, false); } -UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) +UniValue listreceivedbyaccount(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 3) + if (request.fHelp || request.params.size() > 3) throw runtime_error( "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" "\nDEPRECATED. List balances by account.\n" @@ -1304,7 +1311,7 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - return ListReceived(params, true); + return ListReceived(request.params, true); } static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) @@ -1406,12 +1413,12 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Un } } -UniValue listtransactions(const UniValue& params, bool fHelp) +UniValue listtransactions(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 4) + if (request.fHelp || request.params.size() > 4) throw runtime_error( "listtransactions ( \"account\" count from includeWatchonly)\n" "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" @@ -1473,17 +1480,17 @@ UniValue listtransactions(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); + if (request.params.size() > 0) + strAccount = request.params[0].get_str(); int nCount = 10; - if (params.size() > 1) - nCount = params[1].get_int(); + if (request.params.size() > 1) + nCount = request.params[1].get_int(); int nFrom = 0; - if (params.size() > 2) - nFrom = params[2].get_int(); + if (request.params.size() > 2) + nFrom = request.params[2].get_int(); isminefilter filter = ISMINE_SPENDABLE; - if(params.size() > 3) - if(params[3].get_bool()) + if(request.params.size() > 3) + if(request.params[3].get_bool()) filter = filter | ISMINE_WATCH_ONLY; if (nCount < 0) @@ -1533,12 +1540,12 @@ UniValue listtransactions(const UniValue& params, bool fHelp) return ret; } -UniValue listaccounts(const UniValue& params, bool fHelp) +UniValue listaccounts(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 2) + if (request.fHelp || request.params.size() > 2) throw runtime_error( "listaccounts ( minconf includeWatchonly)\n" "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" @@ -1564,11 +1571,11 @@ UniValue listaccounts(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); + if (request.params.size() > 0) + nMinDepth = request.params[0].get_int(); isminefilter includeWatchonly = ISMINE_SPENDABLE; - if(params.size() > 1) - if(params[1].get_bool()) + if(request.params.size() > 1) + if(request.params[1].get_bool()) includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; map<string, CAmount> mapAccountBalances; @@ -1612,12 +1619,12 @@ UniValue listaccounts(const UniValue& params, bool fHelp) return ret; } -UniValue listsinceblock(const UniValue& params, bool fHelp) +UniValue listsinceblock(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp) + if (request.fHelp) throw runtime_error( "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" @@ -1660,26 +1667,26 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; - if (params.size() > 0) + if (request.params.size() > 0) { uint256 blockId; - blockId.SetHex(params[0].get_str()); + blockId.SetHex(request.params[0].get_str()); BlockMap::iterator it = mapBlockIndex.find(blockId); if (it != mapBlockIndex.end()) pindex = it->second; } - if (params.size() > 1) + if (request.params.size() > 1) { - target_confirms = params[1].get_int(); + target_confirms = request.params[1].get_int(); if (target_confirms < 1) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } - if(params.size() > 2) - if(params[2].get_bool()) + if(request.params.size() > 2) + if(request.params[2].get_bool()) filter = filter | ISMINE_WATCH_ONLY; int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; @@ -1704,12 +1711,12 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) return ret; } -UniValue gettransaction(const UniValue& params, bool fHelp) +UniValue gettransaction(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "gettransaction \"txid\" ( includeWatchonly )\n" "\nGet detailed information about in-wallet transaction <txid>\n" @@ -1751,11 +1758,11 @@ UniValue gettransaction(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); uint256 hash; - hash.SetHex(params[0].get_str()); + hash.SetHex(request.params[0].get_str()); isminefilter filter = ISMINE_SPENDABLE; - if(params.size() > 1) - if(params[1].get_bool()) + if(request.params.size() > 1) + if(request.params[1].get_bool()) filter = filter | ISMINE_WATCH_ONLY; UniValue entry(UniValue::VOBJ); @@ -1784,12 +1791,12 @@ UniValue gettransaction(const UniValue& params, bool fHelp) return entry; } -UniValue abandontransaction(const UniValue& params, bool fHelp) +UniValue abandontransaction(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "abandontransaction \"txid\"\n" "\nMark in-wallet transaction <txid> as abandoned\n" @@ -1808,7 +1815,7 @@ UniValue abandontransaction(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); uint256 hash; - hash.SetHex(params[0].get_str()); + hash.SetHex(request.params[0].get_str()); if (!pwalletMain->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); @@ -1819,12 +1826,12 @@ UniValue abandontransaction(const UniValue& params, bool fHelp) } -UniValue backupwallet(const UniValue& params, bool fHelp) +UniValue backupwallet(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 1) + if (request.fHelp || request.params.size() != 1) throw runtime_error( "backupwallet \"destination\"\n" "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n" @@ -1837,7 +1844,7 @@ UniValue backupwallet(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - string strDest = params[0].get_str(); + string strDest = request.params[0].get_str(); if (!pwalletMain->BackupWallet(strDest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); @@ -1845,12 +1852,12 @@ UniValue backupwallet(const UniValue& params, bool fHelp) } -UniValue keypoolrefill(const UniValue& params, bool fHelp) +UniValue keypoolrefill(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 1) + if (request.fHelp || request.params.size() > 1) throw runtime_error( "keypoolrefill ( newsize )\n" "\nFills the keypool." @@ -1866,10 +1873,10 @@ UniValue keypoolrefill(const UniValue& params, bool fHelp) // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool unsigned int kpSize = 0; - if (params.size() > 0) { - if (params[0].get_int() < 0) + if (request.params.size() > 0) { + if (request.params[0].get_int() < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); - kpSize = (unsigned int)params[0].get_int(); + kpSize = (unsigned int)request.params[0].get_int(); } EnsureWalletIsUnlocked(); @@ -1889,12 +1896,12 @@ static void LockWallet(CWallet* pWallet) pWallet->Lock(); } -UniValue walletpassphrase(const UniValue& params, bool fHelp) +UniValue walletpassphrase(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) throw runtime_error( "walletpassphrase \"passphrase\" timeout\n" "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" @@ -1916,17 +1923,17 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (fHelp) + if (request.fHelp) return true; if (!pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); - // Note that the walletpassphrase is stored in params[0] which is not mlock()ed + // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed SecureString strWalletPass; strWalletPass.reserve(100); // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make params[0] mlock()'d to begin with. - strWalletPass = params[0].get_str().c_str(); + // Alternately, find a way to make request.params[0] mlock()'d to begin with. + strWalletPass = request.params[0].get_str().c_str(); if (strWalletPass.length() > 0) { @@ -1940,7 +1947,7 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) pwalletMain->TopUpKeyPool(); - int64_t nSleepTime = params[1].get_int64(); + int64_t nSleepTime = request.params[1].get_int64(); LOCK(cs_nWalletUnlockTime); nWalletUnlockTime = GetTime() + nSleepTime; RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); @@ -1949,12 +1956,12 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) } -UniValue walletpassphrasechange(const UniValue& params, bool fHelp) +UniValue walletpassphrasechange(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) throw runtime_error( "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" @@ -1968,20 +1975,20 @@ UniValue walletpassphrasechange(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (fHelp) + if (request.fHelp) return true; if (!pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) - // Alternately, find a way to make params[0] mlock()'d to begin with. + // Alternately, find a way to make request.params[0] mlock()'d to begin with. SecureString strOldWalletPass; strOldWalletPass.reserve(100); - strOldWalletPass = params[0].get_str().c_str(); + strOldWalletPass = request.params[0].get_str().c_str(); SecureString strNewWalletPass; strNewWalletPass.reserve(100); - strNewWalletPass = params[1].get_str().c_str(); + strNewWalletPass = request.params[1].get_str().c_str(); if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) throw runtime_error( @@ -1995,12 +2002,12 @@ UniValue walletpassphrasechange(const UniValue& params, bool fHelp) } -UniValue walletlock(const UniValue& params, bool fHelp) +UniValue walletlock(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) + if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 0)) throw runtime_error( "walletlock\n" "\nRemoves the wallet encryption key from memory, locking the wallet.\n" @@ -2019,7 +2026,7 @@ UniValue walletlock(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (fHelp) + if (request.fHelp) return true; if (!pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); @@ -2034,12 +2041,12 @@ UniValue walletlock(const UniValue& params, bool fHelp) } -UniValue encryptwallet(const UniValue& params, bool fHelp) +UniValue encryptwallet(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) + if (!pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 1)) throw runtime_error( "encryptwallet \"passphrase\"\n" "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" @@ -2065,16 +2072,16 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (fHelp) + if (request.fHelp) return true; if (pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make params[0] mlock()'d to begin with. + // Alternately, find a way to make request.params[0] mlock()'d to begin with. SecureString strWalletPass; strWalletPass.reserve(100); - strWalletPass = params[0].get_str().c_str(); + strWalletPass = request.params[0].get_str().c_str(); if (strWalletPass.length() < 1) throw runtime_error( @@ -2091,12 +2098,12 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; } -UniValue lockunspent(const UniValue& params, bool fHelp) +UniValue lockunspent(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n" "\nUpdates list of temporarily unspendable outputs.\n" @@ -2135,20 +2142,20 @@ UniValue lockunspent(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (params.size() == 1) - RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); + if (request.params.size() == 1) + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)); else - RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); - bool fUnlock = params[0].get_bool(); + bool fUnlock = request.params[0].get_bool(); - if (params.size() == 1) { + if (request.params.size() == 1) { if (fUnlock) pwalletMain->UnlockAllCoins(); return true; } - UniValue outputs = params[1].get_array(); + UniValue outputs = request.params[1].get_array(); for (unsigned int idx = 0; idx < outputs.size(); idx++) { const UniValue& output = outputs[idx]; if (!output.isObject()) @@ -2180,12 +2187,12 @@ UniValue lockunspent(const UniValue& params, bool fHelp) return true; } -UniValue listlockunspent(const UniValue& params, bool fHelp) +UniValue listlockunspent(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 0) + if (request.fHelp || request.params.size() > 0) throw runtime_error( "listlockunspent\n" "\nReturns list of temporarily unspendable outputs.\n" @@ -2229,12 +2236,12 @@ UniValue listlockunspent(const UniValue& params, bool fHelp) return ret; } -UniValue settxfee(const UniValue& params, bool fHelp) +UniValue settxfee(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) throw runtime_error( "settxfee amount\n" "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" @@ -2250,18 +2257,18 @@ UniValue settxfee(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Amount - CAmount nAmount = AmountFromValue(params[0]); + CAmount nAmount = AmountFromValue(request.params[0]); payTxFee = CFeeRate(nAmount, 1000); return true; } -UniValue getwalletinfo(const UniValue& params, bool fHelp) +UniValue getwalletinfo(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "getwalletinfo\n" "Returns an object containing various wallet state info.\n" @@ -2302,12 +2309,12 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) return obj; } -UniValue resendwallettransactions(const UniValue& params, bool fHelp) +UniValue resendwallettransactions(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() != 0) + if (request.fHelp || request.params.size() != 0) throw runtime_error( "resendwallettransactions\n" "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" @@ -2330,12 +2337,12 @@ UniValue resendwallettransactions(const UniValue& params, bool fHelp) return result; } -UniValue listunspent(const UniValue& params, bool fHelp) +UniValue listunspent(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() > 3) + if (request.fHelp || request.params.size() > 3) throw runtime_error( "listunspent ( minconf maxconf [\"address\",...] )\n" "\nReturns array of unspent transaction outputs\n" @@ -2372,19 +2379,19 @@ UniValue listunspent(const UniValue& params, bool fHelp) + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].get_int(); + if (request.params.size() > 0) + nMinDepth = request.params[0].get_int(); int nMaxDepth = 9999999; - if (params.size() > 1) - nMaxDepth = params[1].get_int(); + if (request.params.size() > 1) + nMaxDepth = request.params[1].get_int(); set<CBitcoinAddress> setAddress; - if (params.size() > 2) { - UniValue inputs = params[2].get_array(); + if (request.params.size() > 2) { + UniValue inputs = request.params[2].get_array(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; CBitcoinAddress address(input.get_str()); @@ -2441,12 +2448,12 @@ UniValue listunspent(const UniValue& params, bool fHelp) return results; } -UniValue fundrawtransaction(const UniValue& params, bool fHelp) +UniValue fundrawtransaction(const JSONRPCRequest& request) { - if (!EnsureWalletIsAvailable(fHelp)) + if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; - if (fHelp || params.size() < 1 || params.size() > 2) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( "fundrawtransaction \"hexstring\" ( options )\n" "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" @@ -2487,7 +2494,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)); CTxDestination changeAddress = CNoDestination(); int changePosition = -1; @@ -2496,15 +2503,15 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) CFeeRate feeRate = CFeeRate(0); bool overrideEstimatedFeerate = false; - if (params.size() > 1) { - if (params[1].type() == UniValue::VBOOL) { + if (request.params.size() > 1) { + if (request.params[1].type() == UniValue::VBOOL) { // backward compatibility bool only fallback - includeWatching = params[1].get_bool(); + includeWatching = request.params[1].get_bool(); } else { - RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); + RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ)); - UniValue options = params[1]; + UniValue options = request.params[1]; RPCTypeCheckObj(options, { @@ -2544,7 +2551,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) // parse hex string from parameter CTransaction origTx; - if (!DecodeHexTx(origTx, params[0].get_str(), true)) + if (!DecodeHexTx(origTx, request.params[0].get_str(), true)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); if (origTx.vout.size() == 0) @@ -2568,14 +2575,14 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) return result; } -extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp -extern UniValue importprivkey(const UniValue& params, bool fHelp); -extern UniValue importaddress(const UniValue& params, bool fHelp); -extern UniValue importpubkey(const UniValue& params, bool fHelp); -extern UniValue dumpwallet(const UniValue& params, bool fHelp); -extern UniValue importwallet(const UniValue& params, bool fHelp); -extern UniValue importprunedfunds(const UniValue& params, bool fHelp); -extern UniValue removeprunedfunds(const UniValue& params, bool fHelp); +extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp +extern UniValue importprivkey(const JSONRPCRequest& request); +extern UniValue importaddress(const JSONRPCRequest& request); +extern UniValue importpubkey(const JSONRPCRequest& request); +extern UniValue dumpwallet(const JSONRPCRequest& request); +extern UniValue importwallet(const JSONRPCRequest& request); +extern UniValue importprunedfunds(const JSONRPCRequest& request); +extern UniValue removeprunedfunds(const JSONRPCRequest& request); static const CRPCCommand commands[] = { // category name actor (function) okSafeMode diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index 05387f5f2b..c5f55ef5f0 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "random.h" +#include "test/test_random.h" #include "utilstrencodings.h" #include "test/test_bitcoin.h" #include "wallet/crypter.h" diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a1f69dd94d..282917d64f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1463,7 +1463,7 @@ void CWallet::ReacceptWalletTransactions() CWalletTx& wtx = *(item.second); LOCK(mempool.cs); - wtx.AcceptToMemoryPool(false, maxTxFee); + wtx.AcceptToMemoryPool(maxTxFee); } } @@ -1907,7 +1907,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns vfBest.assign(vValue.size(), true); nBest = nTotalLower; - seed_insecure_rand(); + FastRandomContext insecure_rand; for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { @@ -1924,7 +1924,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns //that the rng is fast. We do not use a constant random sequence, //because there may be some privacy improvement by making //the selection random. - if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) + if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i]) { nTotal += vValue[i].first; vfIncluded[i] = true; @@ -2502,8 +2502,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon if (fBroadcastTransactions) { // Broadcast - if (!wtxNew.AcceptToMemoryPool(false, maxTxFee)) - { + if (!wtxNew.AcceptToMemoryPool(maxTxFee)) { // This must not fail. The transaction has already been signed and recorded. LogPrintf("CommitTransaction(): Error: Transaction not valid\n"); return false; @@ -3652,8 +3651,8 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee) +bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, *this, true, NULL, false, nAbsurdFee); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 077edcab2d..3b37f7cb1f 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(bool fLimitFree, const CAmount nAbsurdFee); + bool AcceptToMemoryPool(const CAmount& nAbsurdFee); bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } void setAbandoned() { hashBlock = ABANDON_HASH; } diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index 376e7dec59..a0196fe184 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -124,12 +124,15 @@ void CZMQNotificationInterface::Shutdown() } } -void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex) +void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { + if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones + return; + for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); ) { CZMQAbstractNotifier *notifier = *i; - if (notifier->NotifyBlock(pindex)) + if (notifier->NotifyBlock(pindexNew)) { i++; } diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index a853447267..037470ec17 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -25,7 +25,7 @@ protected: // CValidationInterface void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); - void UpdatedBlockTip(const CBlockIndex *pindex); + void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload); private: CZMQNotificationInterface(); |