diff options
Diffstat (limited to 'src')
56 files changed, 1545 insertions, 928 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index bd6f1ba0d5..155adfef7d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,6 +103,7 @@ BITCOIN_CORE_H = \ script/compressor.h \ script/interpreter.h \ script/script.h \ + script/sigcache.h \ script/sign.h \ script/standard.h \ serialize.h \ @@ -218,6 +219,7 @@ libbitcoin_common_a_SOURCES = \ script/compressor.cpp \ script/interpreter.cpp \ script/script.cpp \ + script/sigcache.cpp \ script/sign.cpp \ script/standard.cpp \ $(BITCOIN_CORE_H) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 1ea039adb3..872a0cf1c5 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -178,6 +178,7 @@ BITCOIN_QT_H = \ qt/macdockiconhandler.h \ qt/macnotificationhandler.h \ qt/monitoreddatamapper.h \ + qt/networkstyle.h \ qt/notificator.h \ qt/openuridialog.h \ qt/optionsdialog.h \ @@ -269,6 +270,7 @@ BITCOIN_QT_CPP = \ qt/guiutil.cpp \ qt/intro.cpp \ qt/monitoreddatamapper.cpp \ + qt/networkstyle.cpp \ qt/notificator.cpp \ qt/optionsdialog.cpp \ qt/optionsmodel.cpp \ diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 2cba5b7e1e..23375bef82 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -13,7 +13,7 @@ TEST_QT_H = \ qt/test/paymentservertests.h qt_test_test_bitcoin_qt_CPPFLAGS = $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ - $(QT_INCLUDES) $(QT_TEST_INCLUDES) + $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS) qt_test_test_bitcoin_qt_SOURCES = \ qt/test/test_main.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index ab449f3e71..b20e226c3d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -40,10 +40,10 @@ BITCOIN_TESTS =\ test/base58_tests.cpp \ test/base64_tests.cpp \ test/bloom_tests.cpp \ - test/canonical_tests.cpp \ test/checkblock_tests.cpp \ test/Checkpoints_tests.cpp \ test/compress_tests.cpp \ + test/coins_tests.cpp \ test/crypto_tests.cpp \ test/DoS_tests.cpp \ test/getarg_tests.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp index 3271ecfbfd..d495849206 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -233,25 +233,30 @@ bool CAlert::ProcessAlert(bool fThread) if(AppliesToMe()) { uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); - std::string strCmd = GetArg("-alertnotify", ""); - if (!strCmd.empty()) - { - // Alert text should be plain ascii coming from a trusted source, but to - // be safe we first strip anything not in safeChars, then add single quotes around - // the whole string before passing it to the shell: - std::string singleQuote("'"); - std::string safeStatus = SanitizeString(strStatusBar); - safeStatus = singleQuote+safeStatus+singleQuote; - boost::replace_all(strCmd, "%s", safeStatus); - - if (fThread) - boost::thread t(runCommand, strCmd); // thread runs free - else - runCommand(strCmd); - } + Notify(strStatusBar, fThread); } } LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); return true; } + +void +CAlert::Notify(const std::string& strMessage, bool fThread) +{ + std::string strCmd = GetArg("-alertnotify", ""); + if (strCmd.empty()) return; + + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + std::string safeStatus = SanitizeString(strMessage); + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + if (fThread) + boost::thread t(runCommand, strCmd); // thread runs free + else + runCommand(strCmd); +} diff --git a/src/alert.h b/src/alert.h index 5ecf94cea8..ba3235858d 100644 --- a/src/alert.h +++ b/src/alert.h @@ -101,7 +101,8 @@ public: bool AppliesToMe() const; bool RelayTo(CNode* pnode) const; bool CheckSignature() const; - bool ProcessAlert(bool fThread = true); + bool ProcessAlert(bool fThread = true); // fThread means run -alertnotify in a free-running thread + static void Notify(const std::string& strMessage, bool fThread); /* * Get copy of (active) alert object by hash. Returns a null alert if it is not found. diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index a61b4fe29d..da37e60c7f 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -340,7 +340,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; CCoinsView viewDummy; - CCoinsViewCache view(viewDummy); + CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) throw runtime_error("privatekeys register variable must be set."); @@ -384,21 +384,19 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) vector<unsigned char> pkData(ParseHexUV(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); - CCoins coins; - if (view.GetCoins(txid, coins)) { - if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) { + { + CCoinsModifier coins = view.ModifyCoins(txid); + if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { string err("Previous output scriptPubKey mismatch:\n"); - err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ + err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ scriptPubKey.ToString(); throw runtime_error(err); } - // what todo if txid is known, but the actual output isn't? + if ((unsigned int)nOut >= coins->vout.size()) + coins->vout.resize(nOut+1); + coins->vout[nOut].scriptPubKey = scriptPubKey; + coins->vout[nOut].nValue = 0; // we don't know the actual output value } - if ((unsigned int)nOut >= coins.vout.size()) - coins.vout.resize(nOut+1); - coins.vout[nOut].scriptPubKey = scriptPubKey; - coins.vout[nOut].nValue = 0; // we don't know the actual output value - view.SetCoins(txid, coins); // if redeemScript given and private keys given, // add redeemScript to the tempKeystore so it can be signed: @@ -435,7 +433,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) BOOST_FOREACH(const CTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS)) + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(mergedTx, i))) fComplete = false; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 31c67715c8..f2a14b8293 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -44,6 +44,57 @@ static void convertSeed6(std::vector<CAddress> &vSeedsOut, const SeedSpec6 *data } } + // What makes a good checkpoint block? + // + Is surrounded by blocks with reasonable timestamps + // (no blocks before with a timestamp after, none after with + // timestamp before) + // + Contains no strange transactions +static Checkpoints::MapCheckpoints mapCheckpoints = + boost::assign::map_list_of + ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) + ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) + ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) + (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) + (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")) + (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")) + (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")) + (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) + (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) + (225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) + (250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) + (279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")) + (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")) + ; +static const Checkpoints::CCheckpointData data = { + &mapCheckpoints, + 1397080064, // * UNIX timestamp of last checkpoint block + 36544669, // * total number of transactions between genesis and last checkpoint + // (the tx=... number in the SetBestChain debug.log lines) + 60000.0 // * estimated number of transactions per day after checkpoint + }; + +static Checkpoints::MapCheckpoints mapCheckpointsTestnet = + boost::assign::map_list_of + ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")) + ; +static const Checkpoints::CCheckpointData dataTestnet = { + &mapCheckpointsTestnet, + 1337966069, + 1488, + 300 + }; + +static Checkpoints::MapCheckpoints mapCheckpointsRegtest = + boost::assign::map_list_of + ( 0, uint256("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) + ; +static const Checkpoints::CCheckpointData dataRegtest = { + &mapCheckpointsRegtest, + 0, + 0, + 0 + }; + class CMainParams : public CChainParams { public: CMainParams() { @@ -116,6 +167,12 @@ public: fRequireStandard = true; fMineBlocksOnDemand = false; fSkipProofOfWorkCheck = false; + fTestnetToBeDeprecatedFieldRPC = false; + } + + const Checkpoints::CCheckpointData& Checkpoints() const + { + return data; } }; static CMainParams mainParams; @@ -172,6 +229,11 @@ public: fAllowMinDifficultyBlocks = true; fRequireStandard = false; fMineBlocksOnDemand = false; + fTestnetToBeDeprecatedFieldRPC = true; + } + const Checkpoints::CCheckpointData& Checkpoints() const + { + return dataTestnet; } }; static CTestNetParams testNetParams; @@ -211,6 +273,11 @@ public: fAllowMinDifficultyBlocks = true; fRequireStandard = false; fMineBlocksOnDemand = true; + fTestnetToBeDeprecatedFieldRPC = false; + } + const Checkpoints::CCheckpointData& Checkpoints() const + { + return dataRegtest; } }; static CRegTestParams regTestParams; @@ -233,7 +300,13 @@ public: fAllowMinDifficultyBlocks = false; fMineBlocksOnDemand = true; } -public: + + const Checkpoints::CCheckpointData& Checkpoints() const + { + // UnitTest share the same checkpoints as MAIN + return data; + } + // Published setters to allow changing values in unit test cases virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) { nSubsidyHalvingInterval=anSubsidyHalvingInterval; } virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority) { nEnforceBlockUpgradeMajority=anEnforceBlockUpgradeMajority; } diff --git a/src/chainparams.h b/src/chainparams.h index 50441a89f3..f157419bb2 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -8,6 +8,7 @@ #include "core.h" #include "chainparamsbase.h" +#include "checkpoints.h" #include "protocol.h" #include "uint256.h" @@ -71,12 +72,14 @@ public: /* Make miner stop after a block is found. In RPC, don't return * until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } - CBaseChainParams::Network NetworkID() const { return networkID; } + /* 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; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector<CAddress>& FixedSeeds() const { return vFixedSeeds; } + virtual const Checkpoints::CCheckpointData& Checkpoints() const = 0; protected: CChainParams() {} @@ -106,6 +109,7 @@ protected: bool fRequireStandard; bool fMineBlocksOnDemand; bool fSkipProofOfWorkCheck; + bool fTestnetToBeDeprecatedFieldRPC; }; /** Modifiable parameters interface is used by test cases to adapt the parameters in order diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9a6bc05e63..fbde47339d 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -4,18 +4,16 @@ #include "checkpoints.h" +#include "chainparams.h" #include "main.h" #include "uint256.h" #include <stdint.h> -#include <boost/assign/list_of.hpp> // for 'map_list_of()' #include <boost/foreach.hpp> namespace Checkpoints { - typedef std::map<int, uint256> MapCheckpoints; - // How many times we expect transactions after the last checkpoint to // be slower. This number is a compromise, as it can't be accurate for // every system. When reindexing from a fast disk with a slow CPU, it @@ -23,83 +21,14 @@ namespace Checkpoints { // fast multicore CPU, it won't be much higher than 1. static const double SIGCHECK_VERIFICATION_FACTOR = 5.0; - struct CCheckpointData { - const MapCheckpoints *mapCheckpoints; - int64_t nTimeLastCheckpoint; - int64_t nTransactionsLastCheckpoint; - double fTransactionsPerDay; - }; - bool fEnabled = true; - // What makes a good checkpoint block? - // + Is surrounded by blocks with reasonable timestamps - // (no blocks before with a timestamp after, none after with - // timestamp before) - // + Contains no strange transactions - static MapCheckpoints mapCheckpoints = - boost::assign::map_list_of - ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) - ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) - ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) - (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) - (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")) - (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")) - (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")) - (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) - (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) - (225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) - (250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) - (279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")) - (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")) - ; - static const CCheckpointData data = { - &mapCheckpoints, - 1397080064, // * UNIX timestamp of last checkpoint block - 36544669, // * total number of transactions between genesis and last checkpoint - // (the tx=... number in the SetBestChain debug.log lines) - 60000.0 // * estimated number of transactions per day after checkpoint - }; - - static MapCheckpoints mapCheckpointsTestnet = - boost::assign::map_list_of - ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")) - ; - static const CCheckpointData dataTestnet = { - &mapCheckpointsTestnet, - 1337966069, - 1488, - 300 - }; - - static MapCheckpoints mapCheckpointsRegtest = - boost::assign::map_list_of - ( 0, uint256("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) - ; - static const CCheckpointData dataRegtest = { - &mapCheckpointsRegtest, - 0, - 0, - 0 - }; - - const CCheckpointData &Checkpoints() { - if (Params().NetworkID() == CBaseChainParams::TESTNET) - return dataTestnet; - else if (Params().NetworkID() == CBaseChainParams::MAIN) - return data; - else if (Params().NetworkID() == CBaseChainParams::UNITTEST) // UnitTest share the same checkpoints as MAIN - return data; - else - return dataRegtest; - } - bool CheckBlock(int nHeight, const uint256& hash) { if (!fEnabled) return true; - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints; MapCheckpoints::const_iterator i = checkpoints.find(nHeight); if (i == checkpoints.end()) return true; @@ -119,7 +48,7 @@ namespace Checkpoints { // Work is defined as: 1.0 per transaction before the last checkpoint, and // fSigcheckVerificationFactor per transaction after. - const CCheckpointData &data = Checkpoints(); + const CCheckpointData &data = Params().Checkpoints(); if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { double nCheapBefore = pindex->nChainTx; @@ -143,7 +72,7 @@ namespace Checkpoints { if (!fEnabled) return 0; - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints; return checkpoints.rbegin()->first; } @@ -153,7 +82,7 @@ namespace Checkpoints { if (!fEnabled) return NULL; - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints; BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) { diff --git a/src/checkpoints.h b/src/checkpoints.h index fca046559a..b5b620fa6b 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -5,16 +5,26 @@ #ifndef BITCOIN_CHECKPOINT_H #define BITCOIN_CHECKPOINT_H +#include "uint256.h" + #include <map> class CBlockIndex; -class uint256; /** Block-chain checkpoints are compiled-in sanity checks. * They are updated every release or three. */ namespace Checkpoints { +typedef std::map<int, uint256> MapCheckpoints; + +struct CCheckpointData { + const MapCheckpoints *mapCheckpoints; + int64_t nTimeLastCheckpoint; + int64_t nTransactionsLastCheckpoint; + double fTransactionsPerDay; +}; + // Returns true if block passes checkpoint checks bool CheckBlock(int nHeight, const uint256& hash); diff --git a/src/coins.cpp b/src/coins.cpp index 9b8d63d4e4..e4f3e67aeb 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -53,61 +53,72 @@ bool CCoins::Spend(int nPos) { bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } -bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } uint256 CCoinsView::GetBestBlock() const { return uint256(0); } -bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } -CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); } -bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } -bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); } CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} -CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } +CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), hashBlock(0) { } -bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { - if (cacheCoins.count(txid)) { - coins = cacheCoins[txid]; - return true; - } - if (base->GetCoins(txid, coins)) { - cacheCoins[txid] = coins; - return true; - } - return false; +CCoinsViewCache::~CCoinsViewCache() +{ + assert(!hasModifier); } -CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { +CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { CCoinsMap::iterator it = cacheCoins.find(txid); if (it != cacheCoins.end()) return it; CCoins tmp; - if (!base->GetCoins(txid,tmp)) + if (!base->GetCoins(txid, tmp)) return cacheCoins.end(); - CCoinsMap::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); - tmp.swap(ret->second); + CCoinsMap::iterator ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())).first; + tmp.swap(ret->second.coins); + if (ret->second.coins.IsPruned()) { + // The parent only has an empty entry for this txid; we can consider our + // version as fresh. + ret->second.flags = CCoinsCacheEntry::FRESH; + } return ret; } -CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { - /* Avoid redundant implementation with the const-cast. */ - return const_cast<CCoinsViewCache*>(this)->FetchCoins(txid); +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { + CCoinsMap::const_iterator it = FetchCoins(txid); + if (it != cacheCoins.end()) { + coins = it->second.coins; + return true; + } + return false; } -CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { - CCoinsMap::iterator it = FetchCoins(txid); - assert(it != cacheCoins.end()); - return it->second; +CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { + assert(!hasModifier); + hasModifier = true; + std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); + if (ret.second) { + if (!base->GetCoins(txid, ret.first->second.coins)) { + // The parent view does not have this entry; mark it as fresh. + ret.first->second.coins.Clear(); + ret.first->second.flags = CCoinsCacheEntry::FRESH; + } else if (ret.first->second.coins.IsPruned()) { + // The parent view only has a pruned entry for this; mark it as fresh. + ret.first->second.flags = CCoinsCacheEntry::FRESH; + } + } + // Assume that whenever ModifyCoins is called, the entry will be modified. + ret.first->second.flags |= CCoinsCacheEntry::DIRTY; + return CCoinsModifier(*this, ret.first); } const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { @@ -115,22 +126,17 @@ const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { if (it == cacheCoins.end()) { return NULL; } else { - return &it->second; + return &it->second.coins; } } -bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { - cacheCoins[txid] = coins; - return true; -} - bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); // We're using vtx.empty() instead of IsPruned here for performance reasons, // as we only care about the case where an transaction was replaced entirely // in a reorganization (which wipes vout entirely, as opposed to spending // which just cleans individual outputs). - return (it != cacheCoins.end() && !it->second.vout.empty()); + return (it != cacheCoins.end() && !it->second.coins.vout.empty()); } uint256 CCoinsViewCache::GetBestBlock() const { @@ -139,14 +145,39 @@ uint256 CCoinsViewCache::GetBestBlock() const { return hashBlock; } -bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { +void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { hashBlock = hashBlockIn; - return true; } bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) { + assert(!hasModifier); for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { - cacheCoins[it->first].swap(it->second); + if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization). + CCoinsMap::iterator itUs = cacheCoins.find(it->first); + if (itUs == cacheCoins.end()) { + if (!it->second.coins.IsPruned()) { + // The parent cache does not have an entry, while the child + // cache does have (a non-pruned) one. Move the data up, and + // mark it as fresh (if the grandparent did have it, we + // would have pulled it in at first GetCoins). + assert(it->second.flags & CCoinsCacheEntry::FRESH); + CCoinsCacheEntry& entry = cacheCoins[it->first]; + entry.coins.swap(it->second.coins); + entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; + } + } else { + if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { + // The grandparent does not have an entry, and the child is + // modified and being pruned. This means we can just delete + // it from the parent. + cacheCoins.erase(itUs); + } else { + // A normal modification. + itUs->second.coins.swap(it->second.coins); + itUs->second.flags |= CCoinsCacheEntry::DIRTY; + } + } + } CCoinsMap::iterator itOld = it++; mapCoins.erase(itOld); } @@ -213,3 +244,15 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const } return tx.ComputePriority(dResult); } + +CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_) : cache(cache_), it(it_) {} + +CCoinsModifier::~CCoinsModifier() +{ + assert(cache.hasModifier); + cache.hasModifier = false; + it->second.coins.Cleanup(); + if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { + cache.cacheCoins.erase(it); + } +} diff --git a/src/coins.h b/src/coins.h index 2583475323..b8f1e5bcc5 100644 --- a/src/coins.h +++ b/src/coins.h @@ -83,11 +83,26 @@ public: // as new tx version will probably only be introduced at certain heights int nVersion; - // construct a CCoins from a CTransaction, at a given height - CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { + void FromTx(const CTransaction &tx, int nHeightIn) { + fCoinBase = tx.IsCoinBase(); + vout = tx.vout; + nHeight = nHeightIn; + nVersion = tx.nVersion; ClearUnspendable(); } + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) { + FromTx(tx, nHeightIn); + } + + void Clear() { + fCoinBase = false; + std::vector<CTxOut>().swap(vout); + nHeight = 0; + nVersion = 0; + } + // empty constructor CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } @@ -256,7 +271,20 @@ public: } }; -typedef boost::unordered_map<uint256, CCoins, CCoinsKeyHasher> CCoinsMap; +struct CCoinsCacheEntry +{ + CCoins coins; // The actual cached data. + unsigned char flags; + + enum Flags { + DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view. + FRESH = (1 << 1), // The parent view does not have this entry (or it is pruned). + }; + + CCoinsCacheEntry() : coins(), flags(0) {} +}; + +typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap; struct CCoinsStats { @@ -279,9 +307,6 @@ public: // Retrieve the CCoins (unspent transaction outputs) for a given txid virtual bool GetCoins(const uint256 &txid, CCoins &coins) const; - // Modify the CCoins for a given txid - virtual bool SetCoins(const uint256 &txid, const CCoins &coins); - // Just check whether we have data for a given txid. // This may (but cannot always) return true for fully spent transactions virtual bool HaveCoins(const uint256 &txid) const; @@ -289,10 +314,7 @@ public: // Retrieve the block hash whose state this CCoinsView currently represents virtual uint256 GetBestBlock() const; - // Modify the currently active block hash - virtual bool SetBestBlock(const uint256 &hashBlock); - - // Do a bulk modification (multiple SetCoins + one SetBestBlock). + // Do a bulk modification (multiple CCoins changes + BestBlock change). // The passed mapCoins can be modified. virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); @@ -311,22 +333,41 @@ protected: CCoinsView *base; public: - CCoinsViewBacked(CCoinsView &viewIn); + CCoinsViewBacked(CCoinsView *viewIn); bool GetCoins(const uint256 &txid, CCoins &coins) const; - bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid) const; uint256 GetBestBlock() const; - bool SetBestBlock(const uint256 &hashBlock); void SetBackend(CCoinsView &viewIn); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats) const; }; +class CCoinsViewCache; + +/** A reference to a mutable cache entry. Encapsulating it allows us to run + * cleanup code after the modification is finished, and keeping track of + * concurrent modifications. */ +class CCoinsModifier +{ +private: + CCoinsViewCache& cache; + CCoinsMap::iterator it; + CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_); + +public: + CCoins* operator->() { return &it->second.coins; } + CCoins& operator*() { return it->second.coins; } + ~CCoinsModifier(); + friend class CCoinsViewCache; +}; + /** CCoinsView that adds a memory cache for transactions to another CCoinsView */ class CCoinsViewCache : public CCoinsViewBacked { protected: + /* Whether this cache has an active modifier. */ + bool hasModifier; /* Make mutable so that we can "fill the cache" even from Get-methods declared as "const". */ @@ -334,14 +375,14 @@ protected: mutable CCoinsMap cacheCoins; public: - CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + CCoinsViewCache(CCoinsView *baseIn); + ~CCoinsViewCache(); // Standard CCoinsView methods bool GetCoins(const uint256 &txid, CCoins &coins) const; - bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid) const; uint256 GetBestBlock() const; - bool SetBestBlock(const uint256 &hashBlock); + void SetBestBlock(const uint256 &hashBlock); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); // Return a pointer to CCoins in the cache, or NULL if not found. This is @@ -349,8 +390,10 @@ public: // allowed while accessing the returned pointer. const CCoins* AccessCoins(const uint256 &txid) const; - // Return a modifiable reference to a CCoins. Check HaveCoins first. - CCoins &GetCoins(const uint256 &txid); + // Return a modifiable reference to a CCoins. If no entry with the given + // txid exists, a new one is created. Simultaneous modifications are not + // allowed. + CCoinsModifier ModifyCoins(const uint256 &txid); // Push the modifications applied to this cache to its base. // Failure to call this method before destruction will cause the changes to be forgotten. @@ -377,6 +420,8 @@ public: const CTxOut &GetOutputFor(const CTxIn& input) const; + friend class CCoinsModifier; + private: CCoinsMap::iterator FetchCoins(const uint256 &txid); CCoinsMap::const_iterator FetchCoins(const uint256 &txid) const; diff --git a/src/init.cpp b/src/init.cpp index 980c589b29..8dcd35fb8f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -237,7 +237,7 @@ std::string HelpMessage(HelpMessageMode mode) #if !defined(WIN32) strUsage += " -sysperms " + _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)") + "\n"; #endif - strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; + strUsage += " -txindex " + _("Maintain a full transaction index, used by the getrawtransaction rpc call (default: 0)") + "\n"; strUsage += "\n" + _("Connection options:") + "\n"; strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; @@ -260,7 +260,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS5 proxy") + "\n"; strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; - strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; + strUsage += " -timeout=<n> " + strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT) + "\n"; #ifdef USE_UPNP #if USE_UPNP strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; @@ -280,7 +280,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n"; strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n"; strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + " " + _("on startup") + "\n"; - strUsage += " -respendnotify=<cmd> " + _("Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)") + "\n"; strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup") + "\n"; strUsage += " -spendzeroconfchange " + _("Spend unconfirmed change when sending transactions (default: 1)") + "\n"; strUsage += " -txconfirmtarget=<n> " + _("If paytxfee is not set, include enough fee so transactions are confirmed on average within n blocks (default: 1)") + "\n"; @@ -641,12 +640,9 @@ bool AppInit2(boost::thread_group& threadGroup) bool fDisableWallet = GetBoolArg("-disablewallet", false); #endif - if (mapArgs.count("-timeout")) - { - int nNewTimeout = GetArg("-timeout", 5000); - if (nNewTimeout > 0 && nNewTimeout < 600000) - nConnectTimeout = nNewTimeout; - } + nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); + if (nConnectTimeout <= 0) + nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; // Continue to put "/P2SH/" in the coinbase to monitor // BIP16 support. @@ -959,7 +955,7 @@ bool AppInit2(boost::thread_group& threadGroup) pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); - pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + pcoinsTip = new CCoinsViewCache(pcoinsdbview); if (fReindex) pblocktree->WriteReindexing(true); diff --git a/src/main.cpp b/src/main.cpp index 129d184868..fc8167e40e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,8 +26,8 @@ #include <boost/filesystem/fstream.hpp> #include <boost/thread.hpp> -using namespace std; using namespace boost; +using namespace std; #if defined(NDEBUG) # error "Bitcoin cannot be compiled without assertions." @@ -645,7 +645,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // IsStandard() will have already returned false // and this method isn't called. vector<vector<unsigned char> > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, false)) + if (!EvalScript(stack, tx.vin[i].scriptSig, false, BaseSignatureChecker())) return false; if (whichType == TX_SCRIPTHASH) @@ -844,12 +844,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { CCoinsView dummy; - CCoinsViewCache view(dummy); + CCoinsViewCache view(&dummy); CAmount nValueIn = 0; { LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(*pcoinsTip, pool); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); view.SetBackend(viewMemPool); // do we already have it? @@ -943,7 +943,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS)) + if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } @@ -1178,14 +1178,9 @@ void CheckForkWarningConditions() { if (!fLargeWorkForkFound) { - std::string strCmd = GetArg("-alertnotify", ""); - if (!strCmd.empty()) - { - std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); - boost::replace_all(strCmd, "%s", warning); - boost::thread t(runCommand, strCmd); // thread runs free - } + std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + + pindexBestForkBase->phashBlock->ToString() + std::string("'"); + CAlert::Notify(warning, true); } if (pindexBestForkTip) { @@ -1296,32 +1291,28 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) { - bool ret; // mark inputs spent if (!tx.IsCoinBase()) { txundo.vprevout.reserve(tx.vin.size()); - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxIn &txin = tx.vin[i]; - CCoins &coins = inputs.GetCoins(txin.prevout.hash); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { txundo.vprevout.push_back(CTxInUndo()); - ret = coins.Spend(txin.prevout, txundo.vprevout.back()); + bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout, txundo.vprevout.back()); assert(ret); } } // add outputs - ret = inputs.SetCoins(tx.GetHash(), CCoins(tx, nHeight)); - assert(ret); + inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); } bool CScriptCheck::operator()() const { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags)) + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore))) return error("CScriptCheck() : %s:%d VerifySignature failed", ptxTo->GetHash().ToString(), nIn); return true; } -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks) { if (!tx.IsCoinBase()) { @@ -1390,7 +1381,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi assert(coins); // Verify signature - CScriptCheck check(*coins, tx, i, flags); + CScriptCheck check(*coins, tx, i, flags, cacheStore); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1403,7 +1394,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // avoid splitting the network between upgraded and // non-upgraded nodes. CScriptCheck check(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS); + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); if (check()) return state.Invalid(false, REJECT_NONSTANDARD, "non-mandatory-script-verify-flag"); } @@ -1453,21 +1444,23 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // exactly. Note that transactions with only provably unspendable outputs won't // have outputs available even in the block itself, so we handle that case // specially with outsEmpty. + { CCoins outsEmpty; - CCoins &outs = view.HaveCoins(hash) ? view.GetCoins(hash) : outsEmpty; - outs.ClearUnspendable(); + CCoinsModifier outs = view.ModifyCoins(hash); + outs->ClearUnspendable(); - CCoins outsBlock = CCoins(tx, pindex->nHeight); + CCoins outsBlock(tx, pindex->nHeight); // The CCoins serialization does not serialize negative numbers. // No network rules currently depend on the version here, so an inconsistency is harmless // but it must be corrected before txout nversion ever influences a network rule. if (outsBlock.nVersion < 0) - outs.nVersion = outsBlock.nVersion; - if (outs != outsBlock) + outs->nVersion = outsBlock.nVersion; + if (*outs != outsBlock) fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); // remove outputs - outs = CCoins(); + outs->Clear(); + } // restore inputs if (i > 0) { // not coinbases @@ -1477,27 +1470,24 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex for (unsigned int j = tx.vin.size(); j-- > 0;) { const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; - CCoins coins; - view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent + CCoinsModifier coins = view.ModifyCoins(out.hash); if (undo.nHeight != 0) { // undo data contains height: this is the last output of the prevout tx being spent - if (!coins.IsPruned()) + if (!coins->IsPruned()) fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); - coins = CCoins(); - coins.fCoinBase = undo.fCoinBase; - coins.nHeight = undo.nHeight; - coins.nVersion = undo.nVersion; + coins->Clear(); + coins->fCoinBase = undo.fCoinBase; + coins->nHeight = undo.nHeight; + coins->nVersion = undo.nVersion; } else { - if (coins.IsPruned()) + if (coins->IsPruned()) fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); } - if (coins.IsAvailable(out.n)) + if (coins->IsAvailable(out.n)) fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); - if (coins.vout.size() < out.n+1) - coins.vout.resize(out.n+1); - coins.vout[out.n] = undo.txout; - if (!view.SetCoins(out.hash, coins)) - return error("DisconnectBlock() : cannot restore coin inputs"); + if (coins->vout.size() < out.n+1) + coins->vout.resize(out.n+1); + coins->vout[out.n] = undo.txout; } } } @@ -1599,8 +1589,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C int64_t nBIP16SwitchTime = 1333238400; bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); - unsigned int flags = SCRIPT_VERIFY_NOCACHE | - (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; CBlockUndo blockundo; @@ -1644,7 +1633,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector<CScriptCheck> vChecks; - if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } @@ -1688,7 +1677,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) - return state.Abort(_("Failed to write undo data")); + return state.Abort("Failed to write undo data"); // update nUndoPos in block index pindex->nUndoPos = pos.nPos; @@ -1699,17 +1688,15 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C CDiskBlockIndex blockindex(pindex); if (!pblocktree->WriteBlockIndex(blockindex)) - return state.Abort(_("Failed to write block index")); + return state.Abort("Failed to write block index"); } if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) - return state.Abort(_("Failed to write transaction index")); + return state.Abort("Failed to write transaction index"); // add this block to the view's block chain - bool ret; - ret = view.SetBestBlock(pindex->GetBlockHash()); - assert(ret); + view.SetBestBlock(pindex->GetBlockHash()); int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); @@ -1739,7 +1726,7 @@ bool static WriteChainState(CValidationState &state) { FlushBlockFile(); pblocktree->Sync(); if (!pcoinsTip->Flush()) - return state.Abort(_("Failed to write to coin database")); + return state.Abort("Failed to write to coin database"); nLastWrite = GetTimeMicros(); } return true; @@ -1753,15 +1740,16 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", + LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainActive.Tip())); + Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); cvBlockChange.notify_all(); // Check the version of the last 100 blocks to see if we need to upgrade: - if (!IsInitialBlockDownload()) + static bool fWarned = false; + if (!IsInitialBlockDownload() && !fWarned) { int nUpgraded = 0; const CBlockIndex* pindex = chainActive.Tip(); @@ -1774,8 +1762,12 @@ void static UpdateTip(CBlockIndex *pindexNew) { if (nUpgraded > 0) LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)CBlock::CURRENT_VERSION); if (nUpgraded > 100/2) + { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } } } @@ -1787,11 +1779,11 @@ bool static DisconnectTip(CValidationState &state) { // Read block from disk. CBlock block; if (!ReadBlockFromDisk(block, pindexDelete)) - return state.Abort(_("Failed to read block")); + return state.Abort("Failed to read block"); // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); { - CCoinsViewCache view(*pcoinsTip, true); + CCoinsViewCache view(pcoinsTip); if (!DisconnectBlock(block, state, pindexDelete, view)) return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); assert(view.Flush()); @@ -1836,7 +1828,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * CBlock block; if (!pblock) { if (!ReadBlockFromDisk(block, pindexNew)) - return state.Abort(_("Failed to read block")); + return state.Abort("Failed to read block"); pblock = █ } // Apply the block atomically to the chain state. @@ -1844,7 +1836,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * int64_t nTime3; LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { - CCoinsViewCache view(*pcoinsTip, true); + CCoinsViewCache view(pcoinsTip); CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); if (!ConnectBlock(*pblock, state, pindexNew, view)) { if (state.IsInvalid()) @@ -1990,7 +1982,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo CheckForkWarningConditions(); if (!pblocktree->Flush()) - return state.Abort(_("Failed to sync block index")); + return state.Abort("Failed to sync block index"); return true; } @@ -2027,10 +2019,10 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { // Relay inventory, but don't relay old inventory during initial block download. int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } uiInterface.NotifyBlockTip(hashNewTip); @@ -2097,7 +2089,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl setBlockIndexValid.insert(pindexNew); if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) - return state.Abort(_("Failed to write block index")); + return state.Abort("Failed to write block index"); return true; } @@ -2149,7 +2141,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return state.Abort(_("Failed to write file info")); + return state.Abort("Failed to write file info"); if (fUpdatedLast) pblocktree->WriteLastBlockFile(nLastBlockFile); @@ -2167,15 +2159,15 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne pos.nPos = infoLastBlockFile.nUndoSize; nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return state.Abort(_("Failed to write block info")); + return state.Abort("Failed to write block info"); } else { CBlockFileInfo info; if (!pblocktree->ReadBlockFileInfo(nFile, info)) - return state.Abort(_("Failed to read block info")); + return state.Abort("Failed to read block info"); pos.nPos = info.nUndoSize; nNewSize = (info.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nFile, info)) - return state.Abort(_("Failed to write block info")); + return state.Abort("Failed to write block info"); } unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; @@ -2274,7 +2266,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex if (miSelf != mapBlockIndex.end()) { pindex = miSelf->second; if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("AcceptBlock() : block is marked invalid"), 0, "duplicate"); + return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate"); } CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); @@ -2284,12 +2276,12 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex int64_t deltaTime = block.GetBlockTime() - pcheckpoint->GetBlockTime(); if (deltaTime < 0) { - return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"), + return state.DoS(100, error("%s : block with timestamp before last checkpoint", __func__), REJECT_CHECKPOINT, "time-too-old"); } if (!CheckMinWork(block.nBits, pcheckpoint->nBits, deltaTime)) { - return state.DoS(100, error("CheckBlockHeader() : block with too little proof-of-work"), + return state.DoS(100, error("%s : block with too little proof-of-work", __func__), REJECT_INVALID, "bad-diffbits"); } } @@ -2300,36 +2292,36 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex if (hash != Params().HashGenesisBlock()) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) - return state.DoS(10, error("AcceptBlock() : prev block not found"), 0, "bad-prevblk"); + return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk"); pindexPrev = (*mi).second; nHeight = pindexPrev->nHeight+1; // Check proof of work if ((!Params().SkipProofOfWorkCheck()) && (block.nBits != GetNextWorkRequired(pindexPrev, &block))) - return state.DoS(100, error("AcceptBlock() : incorrect proof of work"), + return state.DoS(100, error("%s : incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("AcceptBlock() : block's timestamp is too early"), + return state.Invalid(error("%s : block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight), + return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), REJECT_CHECKPOINT, "checkpoint mismatch"); // Don't accept any forks from the main chain prior to last checkpoint CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); + return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2 && CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) { - return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), + return state.Invalid(error("%s : rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); } } @@ -2392,11 +2384,11 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return error("AcceptBlock() : FindBlockPos failed"); if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos)) - return state.Abort(_("Failed to write block")); + return state.Abort("Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock() : ReceivedBlockTransactions failed"); } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); + return state.Abort(std::string("System error: ") + e.what()); } return true; @@ -2719,10 +2711,12 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { -bool AbortNode(const std::string &strMessage) { +bool AbortNode(const std::string &strMessage, const std::string &userMessage) { strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); + uiInterface.ThreadSafeMessageBox( + userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; } @@ -2733,7 +2727,7 @@ bool CheckDiskSpace(uint64_t nAdditionalBytes) // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) - return AbortNode(_("Error: Disk space is low!")); + return AbortNode("Disk space is low!", _("Error: Disk space is low!")); return true; } @@ -2892,7 +2886,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth nCheckDepth = chainActive.Height(); nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CCoinsViewCache coins(*coinsview, true); + CCoinsViewCache coins(coinsview); CBlockIndex* pindexState = chainActive.Tip(); CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; @@ -3143,7 +3137,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } } } catch(std::runtime_error &e) { - AbortNode(_("Error: system error: ") + e.what()); + AbortNode(std::string("System error: ") + e.what()); } if (nLoaded > 0) LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); diff --git a/src/main.h b/src/main.h index 7e849505dd..cad7eebfb7 100644 --- a/src/main.h +++ b/src/main.h @@ -17,6 +17,7 @@ #include "net.h" #include "pow.h" #include "script/script.h" +#include "script/sigcache.h" #include "script/standard.h" #include "sync.h" #include "txmempool.h" @@ -177,7 +178,7 @@ CAmount GetBlockValue(int nHeight, const CAmount& nFees); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); /** Abort with a message */ -bool AbortNode(const std::string &msg); +bool AbortNode(const std::string &msg, const std::string &userMessage=""); /** Get statistics from node state */ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** Increase a node's misbehavior score. */ @@ -258,9 +259,8 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it // instead of being performed inline. -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks = true, - unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS, - std::vector<CScriptCheck> *pvChecks = NULL); +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, + unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks = NULL); // Apply the effects of this transaction on the UTXO set represented by view void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight); @@ -302,12 +302,13 @@ private: const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; + bool cacheStore; public: - CScriptCheck(): ptxTo(0), nIn(0), nFlags(0) {} - CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn) : + CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false) {} + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) : scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn) { } + ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn) { } bool operator()() const; @@ -316,6 +317,7 @@ public: std::swap(ptxTo, check.ptxTo); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); + std::swap(cacheStore, check.cacheStore); } }; diff --git a/src/miner.cpp b/src/miner.cpp index 361a2bea41..c2762bf44e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -83,6 +83,11 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (Params().MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + // Create coinbase tx CMutableTransaction txNew; txNew.vin.resize(1); @@ -116,7 +121,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) { LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); - CCoinsViewCache view(*pcoinsTip, true); + CCoinsViewCache view(pcoinsTip); // Priority order to process transactions list<COrphan> vOrphan; // list memory doesn't move @@ -257,7 +262,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // policy here, but we still have to ensure that the block we // create only contains transactions that are valid in new blocks. CValidationState state; - if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS)) + if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) continue; CTxUndo txundo; @@ -316,7 +321,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; - CCoinsViewCache viewNew(*pcoinsTip, true); + CCoinsViewCache viewNew(pcoinsTip); CValidationState state; if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); diff --git a/src/netbase.cpp b/src/netbase.cpp index 5819c152a3..b3d1001547 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -40,7 +40,7 @@ using namespace std; static proxyType proxyInfo[NET_MAX]; static CService nameProxy; static CCriticalSection cs_proxyInfos; -int nConnectTimeout = 5000; +int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; bool fNameLookup = false; static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; diff --git a/src/netbase.h b/src/netbase.h index 9fc5c72eb8..1455cd8c33 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -19,6 +19,9 @@ extern int nConnectTimeout; extern bool fNameLookup; +/** -timeout default */ +static const int DEFAULT_CONNECT_TIMEOUT = 5000; + #ifdef WIN32 // In MSVC, this is defined as a macro, undefine it to prevent a compile and link error #undef SetPort diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 5e2fdc6c30..9872ebc1f6 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -12,6 +12,7 @@ #include "guiconstants.h" #include "guiutil.h" #include "intro.h" +#include "networkstyle.h" #include "optionsmodel.h" #include "splashscreen.h" #include "utilitydialog.h" @@ -190,9 +191,9 @@ public: /// Create options model void createOptionsModel(); /// Create main window - void createWindow(bool isaTestNet); + void createWindow(const NetworkStyle *networkStyle); /// Create splash screen - void createSplashScreen(bool isaTestNet); + void createSplashScreen(const NetworkStyle *networkStyle); /// Request core initialization void requestInitialize(); @@ -331,18 +332,18 @@ void BitcoinApplication::createOptionsModel() optionsModel = new OptionsModel(); } -void BitcoinApplication::createWindow(bool isaTestNet) +void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) { - window = new BitcoinGUI(isaTestNet, 0); + window = new BitcoinGUI(networkStyle, 0); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); pollShutdownTimer->start(200); } -void BitcoinApplication::createSplashScreen(bool isaTestNet) +void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { - SplashScreen *splash = new SplashScreen(0, isaTestNet); + SplashScreen *splash = new SplashScreen(0, networkStyle); // We don't hold a direct pointer to the splash screen after creation, so use // Qt::WA_DeleteOnClose to make sure that the window will be deleted eventually. splash->setAttribute(Qt::WA_DeleteOnClose); @@ -572,12 +573,10 @@ int main(int argc, char *argv[]) if (!PaymentServer::ipcParseCommandLine(argc, argv)) exit(0); #endif - bool isaTestNet = Params().NetworkID() != CBaseChainParams::MAIN; + QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString()))); + assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets - if (isaTestNet) - QApplication::setApplicationName(QAPP_APP_NAME_TESTNET); - else - QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); + QApplication::setApplicationName(networkStyle->getAppName()); // Re-initialize translations after changing application name (language in network-specific settings can be different) initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); @@ -617,11 +616,11 @@ int main(int argc, char *argv[]) uiInterface.InitMessage.connect(InitMessage); if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) - app.createSplashScreen(isaTestNet); + app.createSplashScreen(networkStyle.data()); try { - app.createWindow(isaTestNet); + app.createWindow(networkStyle.data()); app.requestInitialize(); #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("Bitcoin Core didn't yet exit safely..."), (HWND)app.getMainWinId()); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 7380fbd240..8a945606dc 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -8,6 +8,7 @@ #include "clientmodel.h" #include "guiconstants.h" #include "guiutil.h" +#include "networkstyle.h" #include "notificator.h" #include "openuridialog.h" #include "optionsdialog.h" @@ -59,7 +60,7 @@ const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; -BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : +BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), clientModel(0), walletFrame(0), @@ -112,26 +113,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : } else { windowTitle += tr("Node"); } - - if (!fIsTestnet) - { + windowTitle += " " + networkStyle->getTitleAddText(); #ifndef Q_OS_MAC - QApplication::setWindowIcon(QIcon(":icons/bitcoin")); - setWindowIcon(QIcon(":icons/bitcoin")); + QApplication::setWindowIcon(networkStyle->getAppIcon()); + setWindowIcon(networkStyle->getAppIcon()); #else - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); + MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon()); #endif - } - else - { - windowTitle += " " + tr("[testnet]"); -#ifndef Q_OS_MAC - QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); - setWindowIcon(QIcon(":icons/bitcoin_testnet")); -#else - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); -#endif - } setWindowTitle(windowTitle); #if defined(Q_OS_MAC) && QT_VERSION < 0x050000 @@ -161,7 +149,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : // Create actions for the toolbar, menu bar and tray/dock icon // Needs walletFrame to be initialized - createActions(fIsTestnet); + createActions(networkStyle); // Create application menu bar createMenuBar(); @@ -170,7 +158,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : createToolBars(); // Create system tray icon and notification - createTrayIcon(fIsTestnet); + createTrayIcon(networkStyle); // Create status bar statusBar(); @@ -248,7 +236,7 @@ BitcoinGUI::~BitcoinGUI() #endif } -void BitcoinGUI::createActions(bool fIsTestnet) +void BitcoinGUI::createActions(const NetworkStyle *networkStyle) { QActionGroup *tabGroup = new QActionGroup(this); @@ -295,10 +283,7 @@ void BitcoinGUI::createActions(bool fIsTestnet) quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); - if (!fIsTestnet) - aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin Core"), this); - else - aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin Core"), this); + aboutAction = new QAction(networkStyle->getAppIcon(), tr("&About Bitcoin Core"), this); aboutAction->setStatusTip(tr("Show information about Bitcoin Core")); aboutAction->setMenuRole(QAction::AboutRole); #if QT_VERSION < 0x050000 @@ -311,10 +296,7 @@ void BitcoinGUI::createActions(bool fIsTestnet) optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin")); optionsAction->setMenuRole(QAction::PreferencesRole); - if (!fIsTestnet) - toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); - else - toggleHideAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&Show / Hide"), this); + toggleHideAction = new QAction(networkStyle->getAppIcon(), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); @@ -505,22 +487,13 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) openAction->setEnabled(enabled); } -void BitcoinGUI::createTrayIcon(bool fIsTestnet) +void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) { #ifndef Q_OS_MAC trayIcon = new QSystemTrayIcon(this); - - if (!fIsTestnet) - { - trayIcon->setToolTip(tr("Bitcoin Core client")); - trayIcon->setIcon(QIcon(":/icons/bitcoin")); - } - else - { - trayIcon->setToolTip(tr("Bitcoin Core client") + " " + tr("[testnet]")); - trayIcon->setIcon(QIcon(":/icons/bitcoin_testnet")); - } - + QString toolTip = tr("Bitcoin Core client") + " " + networkStyle->getTitleAddText(); + trayIcon->setToolTip(toolTip); + trayIcon->setIcon(networkStyle->getAppIcon()); trayIcon->show(); #endif diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8af6eda867..f65f0e9137 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -19,6 +19,7 @@ #include <QSystemTrayIcon> class ClientModel; +class NetworkStyle; class Notificator; class OptionsModel; class RPCConsole; @@ -46,7 +47,7 @@ class BitcoinGUI : public QMainWindow public: static const QString DEFAULT_WALLET; - explicit BitcoinGUI(bool fIsTestnet = false, QWidget *parent = 0); + explicit BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -114,13 +115,13 @@ private: int spinnerFrame; /** Create the main UI actions. */ - void createActions(bool fIsTestnet); + void createActions(const NetworkStyle *networkStyle); /** Create the menu bar and sub-menus. */ void createMenuBar(); /** Create the toolbars */ void createToolBars(); /** Create system tray icon and notification */ - void createTrayIcon(bool fIsTestnet); + void createTrayIcon(const NetworkStyle *networkStyle); /** Create system tray menu (or setup the dock menu) */ void createTrayIconMenu(); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 3b4b40aae3..25c811183f 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -195,21 +195,11 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted"), QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires newer version of Bitcoin Core"), QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occured, see debug.log for details"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unsupported argument -tor found, use -onion."), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"), -QT_TRANSLATE_NOOP("bitcoin-core", "Error: system error: "), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block info"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to sync block index"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block index"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block info"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"), -QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"), QT_TRANSLATE_NOOP("bitcoin-core", "Fee (in BTC/kB) to add to transactions you send (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Force safe mode (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: 0)"), @@ -231,6 +221,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'") QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable blocks in memory (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable transactions in memory (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Limit size of signature cache to <n> entries (default: 50000)"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: 8333 or testnet: 18333)"), QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), @@ -245,7 +236,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Need to specify a port with -whitebind: '%s'" QT_TRANSLATE_NOOP("bitcoin-core", "Node relay options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."), QT_TRANSLATE_NOOP("bitcoin-core", "Only accept block chain matching built-in checkpoints (default: 1)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (IPv4, IPv6 or Tor)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (ipv4, ipv6 or onion)"), QT_TRANSLATE_NOOP("bitcoin-core", "Options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default: 1)"), @@ -282,7 +273,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)") QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), QT_TRANSLATE_NOOP("bitcoin-core", "Spend unconfirmed change when sending transactions (default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Stop running after importing blocks from disk (default: 0)"), -QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."), QT_TRANSLATE_NOOP("bitcoin-core", "This is intended for regression testing tools and app development."), diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index a527602b5a..5c3abef2e7 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -19,7 +19,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+14"/> <source>Copy the currently selected address to the system clipboard</source> <translation>Copy the currently selected address to the system clipboard</translation> </message> @@ -29,7 +29,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+58"/> + <location line="+67"/> <source>C&lose</source> <translation type="unfinished"></translation> </message> @@ -39,12 +39,12 @@ <translation>&Copy Address</translation> </message> <message> - <location filename="../forms/addressbookpage.ui" line="-47"/> + <location filename="../forms/addressbookpage.ui" line="-53"/> <source>Delete the currently selected address from the list</source> <translation>Delete the currently selected address from the list</translation> </message> <message> - <location line="+27"/> + <location line="+30"/> <source>Export the data in the current tab to a file</source> <translation>Export the data in the current tab to a file</translation> </message> @@ -54,7 +54,7 @@ <translation>&Export</translation> </message> <message> - <location line="-27"/> + <location line="-30"/> <source>&Delete</source> <translation>&Delete</translation> </message> @@ -286,17 +286,17 @@ <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+300"/> + <location filename="../bitcoingui.cpp" line="+327"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+339"/> + <location line="+348"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-411"/> + <location line="-420"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -377,13 +377,13 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+168"/> + <location line="+175"/> <location line="+5"/> <source>Bitcoin Core client</source> <translation type="unfinished"></translation> </message> <message> - <location line="+156"/> + <location line="+158"/> <source>Importing blocks from disk...</source> <translation>Importing blocks from disk...</translation> </message> @@ -393,7 +393,7 @@ <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-409"/> + <location line="-418"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> @@ -428,12 +428,12 @@ <translation>&Verify message...</translation> </message> <message> - <location line="+437"/> + <location line="+446"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-655"/> + <location line="-664"/> <source>Wallet</source> <translation>Wallet</translation> </message> @@ -500,12 +500,12 @@ </message> <message> <location line="-289"/> - <location line="+386"/> + <location line="+393"/> <source>[testnet]</source> <translation>[testnet]</translation> </message> <message> - <location line="-411"/> + <location line="-418"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -546,7 +546,7 @@ <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+310"/> + <location line="+316"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -554,7 +554,7 @@ </translation> </message> <message> - <location line="+22"/> + <location line="+25"/> <source>No block source available...</source> <translation>No block source available...</translation> </message> @@ -665,7 +665,7 @@ Address: %4 </translation> </message> <message> - <location line="+69"/> + <location line="+68"/> <source>Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> <translation>Wallet is <b>encrypted</b> and currently <b>unlocked</b></translation> </message> @@ -678,7 +678,7 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+138"/> + <location filename="../clientmodel.cpp" line="+139"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -736,7 +736,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> + <location line="+16"/> <source>Tree mode</source> <translation type="unfinished"></translation> </message> @@ -1059,7 +1059,7 @@ Address: %4 <context> <name>HelpMessageDialog</name> <message> - <location filename="../utilitydialog.cpp" line="+29"/> + <location filename="../utilitydialog.cpp" line="+31"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -1201,7 +1201,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+10"/> <source>Select payment request file</source> <translation type="unfinished"></translation> </message> @@ -1430,12 +1430,12 @@ Address: %4 <translation>&OK</translation> </message> <message> - <location line="+7"/> + <location line="+13"/> <source>&Cancel</source> <translation>&Cancel</translation> </message> <message> - <location filename="../optionsdialog.cpp" line="+68"/> + <location filename="../optionsdialog.cpp" line="+71"/> <source>default</source> <translation>default</translation> </message> @@ -1479,23 +1479,18 @@ Address: %4 <translation>Form</translation> </message> <message> - <location line="+52"/> - <location line="+394"/> + <location line="+53"/> + <location line="+372"/> <source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source> <translation>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</translation> </message> <message> - <location line="-401"/> - <source>Wallet</source> - <translation>Wallet</translation> - </message> - <message> - <location line="+33"/> + <location line="-133"/> <source>Watch-only:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+43"/> + <location line="+10"/> <source>Available:</source> <translation type="unfinished"></translation> </message> @@ -1505,62 +1500,72 @@ Address: %4 <translation>Your current spendable balance</translation> </message> <message> - <location line="+16"/> + <location line="+41"/> <source>Pending:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+16"/> + <location line="-236"/> <source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source> <translation>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</translation> </message> <message> - <location line="+16"/> + <location line="+112"/> <source>Immature:</source> <translation>Immature:</translation> </message> <message> - <location line="+16"/> + <location line="-29"/> <source>Mined balance that has not yet matured</source> <translation>Mined balance that has not yet matured</translation> </message> <message> - <location line="+23"/> + <location line="-163"/> + <source>Balances</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+147"/> <source>Total:</source> <translation>Total:</translation> </message> <message> - <location line="+16"/> + <location line="+61"/> <source>Your current total balance</source> <translation>Your current total balance</translation> </message> <message> - <location line="+38"/> + <location line="+92"/> <source>Your current balance in watch-only addresses</source> <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+23"/> + <source>Spendable:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+49"/> + <source>Recent transactions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-317"/> <source>Unconfirmed transactions to watch-only addresses</source> <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+50"/> <source>Mined balance in watch-only addresses that has not yet matured</source> <translation type="unfinished"></translation> </message> <message> - <location line="+44"/> + <location line="+128"/> <source>Current total balance in watch-only addresses</source> <translation type="unfinished"></translation> </message> <message> - <location line="+67"/> - <source><b>Recent transactions</b></source> - <translation><b>Recent transactions</b></translation> - </message> - <message> - <location filename="../overviewpage.cpp" line="+123"/> + <location filename="../overviewpage.cpp" line="+131"/> <location line="+1"/> <source>out of sync</source> <translation>out of sync</translation> @@ -1569,7 +1574,7 @@ Address: %4 <context> <name>PaymentServer</name> <message> - <location filename="../paymentserver.cpp" line="+405"/> + <location filename="../paymentserver.cpp" line="+410"/> <location line="+14"/> <location line="+7"/> <source>URI handling</source> @@ -1681,7 +1686,7 @@ Address: %4 <context> <name>PeerTableModel</name> <message> - <location filename="../peertablemodel.cpp" line="+112"/> + <location filename="../peertablemodel.cpp" line="+118"/> <source>User Agent</source> <translation type="unfinished"></translation> </message> @@ -1699,17 +1704,17 @@ Address: %4 <context> <name>QObject</name> <message> - <location filename="../bitcoinunits.cpp" line="+200"/> + <location filename="../bitcoinunits.cpp" line="+196"/> <source>Amount</source> <translation type="unfinished">Amount</translation> </message> <message> - <location filename="../guiutil.cpp" line="+97"/> + <location filename="../guiutil.cpp" line="+106"/> <source>Enter a Bitcoin address (e.g. %1)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+673"/> + <location line="+698"/> <source>%1 d</source> <translation type="unfinished"></translation> </message> @@ -1795,7 +1800,7 @@ Address: %4 <location line="+23"/> <location line="+36"/> <location line="+23"/> - <location line="+462"/> + <location line="+465"/> <location line="+23"/> <location line="+23"/> <location line="+23"/> @@ -1813,7 +1818,7 @@ Address: %4 <translation>N/A</translation> </message> <message> - <location line="-987"/> + <location line="-990"/> <source>Client version</source> <translation>Client version</translation> </message> @@ -1873,7 +1878,7 @@ Address: %4 <translation>Current number of blocks</translation> </message> <message> - <location line="+300"/> + <location line="+303"/> <source>Received</source> <translation type="unfinished"></translation> </message> @@ -1889,7 +1894,7 @@ Address: %4 </message> <message> <location line="+39"/> - <location filename="../rpcconsole.cpp" line="+234"/> + <location filename="../rpcconsole.cpp" line="+236"/> <location line="+327"/> <source>Select a peer to view detailed information.</source> <translation type="unfinished"></translation> @@ -1965,7 +1970,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="-761"/> + <location line="-764"/> <source>Last block time</source> <translation>Last block time</translation> </message> @@ -1990,7 +1995,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> + <location line="+16"/> <source>Totals</source> <translation type="unfinished"></translation> </message> @@ -2005,7 +2010,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location filename="../forms/rpcconsole.ui" line="-354"/> + <location filename="../forms/rpcconsole.ui" line="-357"/> <source>Build date</source> <translation>Build date</translation> </message> @@ -2163,12 +2168,12 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+78"/> + <location line="+75"/> <source>Requested payments history</source> <translation type="unfinished"></translation> </message> <message> - <location line="-98"/> + <location line="-95"/> <source>&Request payment</source> <translation type="unfinished"></translation> </message> @@ -2183,7 +2188,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> + <location line="+17"/> <source>Remove the selected entries from the list</source> <translation type="unfinished"></translation> </message> @@ -2221,12 +2226,12 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+10"/> <source>Copy &Address</source> <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+10"/> <source>&Save Image...</source> <translation type="unfinished"></translation> </message> @@ -2279,7 +2284,7 @@ Address: %4 <context> <name>RecentRequestsTableModel</name> <message> - <location filename="../recentrequeststablemodel.cpp" line="+24"/> + <location filename="../recentrequeststablemodel.cpp" line="+26"/> <source>Date</source> <translation type="unfinished">Date</translation> </message> @@ -2333,7 +2338,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+10"/> <source>automatically selected</source> <translation type="unfinished"></translation> </message> @@ -2398,22 +2403,22 @@ Address: %4 <translation>Add &Recipient</translation> </message> <message> - <location line="-23"/> + <location line="-20"/> <source>Clear all fields of the form.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-271"/> + <location line="-274"/> <source>Dust:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+274"/> + <location line="+277"/> <source>Clear &All</source> <translation>Clear &All</translation> </message> <message> - <location line="+58"/> + <location line="+55"/> <source>Balance:</source> <translation>Balance:</translation> </message> @@ -2653,7 +2658,7 @@ Address: %4 <context> <name>ShutdownWindow</name> <message> - <location filename="../utilitydialog.cpp" line="+51"/> + <location filename="../utilitydialog.cpp" line="+47"/> <source>Bitcoin Core is shutting down...</source> <translation type="unfinished"></translation> </message> @@ -2671,7 +2676,7 @@ Address: %4 <translation>Signatures - Sign / Verify a Message</translation> </message> <message> - <location line="+10"/> + <location line="+13"/> <source>&Sign Message</source> <translation>&Sign Message</translation> </message> @@ -2848,7 +2853,7 @@ Address: %4 <context> <name>SplashScreen</name> <message> - <location filename="../splashscreen.cpp" line="+32"/> + <location filename="../splashscreen.cpp" line="+34"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -2874,7 +2879,7 @@ Address: %4 <context> <name>TransactionDesc</name> <message> - <location filename="../transactiondesc.cpp" line="+33"/> + <location filename="../transactiondesc.cpp" line="+34"/> <source>Open until %1</source> <translation>Open until %1</translation> </message> @@ -3098,7 +3103,7 @@ Address: %4 <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+237"/> + <location filename="../transactiontablemodel.cpp" line="+235"/> <source>Date</source> <translation>Date</translation> </message> @@ -3166,7 +3171,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="+51"/> + <location line="+48"/> <source>Received with</source> <translation>Received with</translation> </message> @@ -3191,12 +3196,17 @@ Address: %4 <translation>Mined</translation> </message> <message> - <location line="+41"/> + <location line="+28"/> + <source>watch-only</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+15"/> <source>(n/a)</source> <translation>(n/a)</translation> </message> <message> - <location line="+193"/> + <location line="+210"/> <source>Transaction status. Hover over this field to show number of confirmations.</source> <translation>Transaction status. Hover over this field to show number of confirmations.</translation> </message> @@ -3212,6 +3222,11 @@ Address: %4 </message> <message> <location line="+2"/> + <source>Whether or not a watch-only address is involved in this transaction.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Destination address of transaction.</source> <translation>Destination address of transaction.</translation> </message> @@ -3224,7 +3239,7 @@ Address: %4 <context> <name>TransactionView</name> <message> - <location filename="../transactionview.cpp" line="+60"/> + <location filename="../transactionview.cpp" line="+67"/> <location line="+16"/> <source>All</source> <translation>All</translation> @@ -3325,12 +3340,17 @@ Address: %4 <translation>Show transaction details</translation> </message> <message> - <location line="+163"/> + <location line="+179"/> <source>Export Transaction History</source> <translation type="unfinished"></translation> </message> <message> - <location line="+19"/> + <location line="+12"/> + <source>Watch-only</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> <source>Exporting Failed</source> <translation type="unfinished"></translation> </message> @@ -3350,7 +3370,7 @@ Address: %4 <translation type="unfinished"></translation> </message> <message> - <location line="-22"/> + <location line="-24"/> <source>Comma separated file (*.csv)</source> <translation>Comma separated file (*.csv)</translation> </message> @@ -3360,7 +3380,7 @@ Address: %4 <translation>Confirmed</translation> </message> <message> - <location line="+1"/> + <location line="+3"/> <source>Date</source> <translation>Date</translation> </message> @@ -3398,7 +3418,7 @@ Address: %4 <context> <name>UnitDisplayStatusBarControl</name> <message> - <location filename="../bitcoingui.cpp" line="+101"/> + <location filename="../bitcoingui.cpp" line="+103"/> <source>Unit to show amounts in. Click to select another unit.</source> <translation type="unfinished"></translation> </message> @@ -3465,7 +3485,7 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+249"/> + <location filename="../bitcoinstrings.cpp" line="+240"/> <source>Options:</source> <translation>Options:</translation> </message> @@ -3495,22 +3515,22 @@ Address: %4 <translation>Maintain at most <n> connections to peers (default: 125)</translation> </message> <message> - <location line="-62"/> + <location line="-53"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+103"/> + <location line="+94"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="+7"/> + <location line="+6"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-181"/> + <location line="-171"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation> </message> @@ -3525,17 +3545,17 @@ Address: %4 <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+99"/> + <location line="+90"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-134"/> + <location line="-124"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> @@ -3756,6 +3776,11 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. </message> <message> <location line="+2"/> + <source>Error: A fatal internal error occured, see debug.log for details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Error: Disk space is low!</source> <translation>Error: Disk space is low!</translation> </message> @@ -3766,65 +3791,10 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. </message> <message> <location line="+1"/> - <source>Error: system error: </source> - <translation>Error: system error: </translation> - </message> - <message> - <location line="+1"/> <source>Failed to listen on any port. Use -listen=0 if you want this.</source> <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation> </message> <message> - <location line="+1"/> - <source>Failed to read block info</source> - <translation>Failed to read block info</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to read block</source> - <translation>Failed to read block</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to sync block index</source> - <translation>Failed to sync block index</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write block index</source> - <translation>Failed to write block index</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write block info</source> - <translation>Failed to write block info</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write block</source> - <translation>Failed to write block</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write file info</source> - <translation>Failed to write file info</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write to coin database</source> - <translation>Failed to write to coin database</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write transaction index</source> - <translation>Failed to write transaction index</translation> - </message> - <message> - <location line="+1"/> - <source>Failed to write undo data</source> - <translation>Failed to write undo data</translation> - </message> - <message> <location line="+2"/> <source>Force safe mode (default: 0)</source> <translation type="unfinished"></translation> @@ -3860,12 +3830,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation type="unfinished"></translation> </message> <message> - <location line="+21"/> + <location line="+22"/> <source>Not enough file descriptors available.</source> <translation>Not enough file descriptors available.</translation> </message> <message> - <location line="+5"/> + <location line="+2"/> + <source>Only connect to nodes in network <net> (ipv4, ipv6 or onion)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Prepend debug output with timestamp (default: 1)</source> <translation type="unfinished"></translation> </message> @@ -3905,7 +3880,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+3"/> <source>This is intended for regression testing tools and app development.</source> <translation type="unfinished"></translation> </message> @@ -3940,7 +3915,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Imports blocks from external blk000??.dat file</translation> </message> <message> - <location line="-195"/> + <location line="-185"/> <source>(default: 1, 1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source> <translation type="unfinished"></translation> </message> @@ -4080,12 +4055,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+5"/> <source>Error: Unsupported argument -tor found, use -onion.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> + <location line="+3"/> <source>Fee (in BTC/kB) to add to transactions you send (default: %s)</source> <translation type="unfinished"></translation> </message> @@ -4131,6 +4106,11 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. </message> <message> <location line="+1"/> + <source>Keep at most <n> unconnectable transactions in memory (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Limit size of signature cache to <n> entries (default: 50000)</source> <translation type="unfinished"></translation> </message> @@ -4170,12 +4150,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Only accept block chain matching built-in checkpoints (default: 1)</translation> </message> <message> - <location line="+1"/> - <source>Only connect to nodes in network <net> (IPv4, IPv6 or Tor)</source> - <translation>Only connect to nodes in network <net> (IPv4, IPv6 or Tor)</translation> - </message> - <message> - <location line="+4"/> + <location line="+5"/> <source>Print block on startup, if found in block index</source> <translation type="unfinished"></translation> </message> @@ -4255,12 +4230,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Specify connection timeout in milliseconds (default: 5000)</translation> </message> <message> - <location line="+7"/> - <source>System error: </source> - <translation>System error: </translation> - </message> - <message> - <location line="+2"/> + <location line="+8"/> <source>This is experimental software.</source> <translation type="unfinished"></translation> </message> @@ -4340,22 +4310,22 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-64"/> + <location line="-63"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-164"/> + <location line="-155"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+210"/> + <location line="+200"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> <message> - <location line="-27"/> + <location line="-26"/> <source>Set key pool size to <n> (default: 100)</source> <translation>Set key pool size to <n> (default: 100)</translation> </message> @@ -4365,12 +4335,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Rescan the block chain for missing wallet transactions</translation> </message> <message> - <location line="+36"/> + <location line="+35"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Use OpenSSL (https) for JSON-RPC connections</translation> </message> <message> - <location line="-31"/> + <location line="-30"/> <source>Server certificate file (default: server.cert)</source> <translation>Server certificate file (default: server.cert)</translation> </message> @@ -4380,22 +4350,22 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Server private key (default: server.pem)</translation> </message> <message> - <location line="+19"/> + <location line="+18"/> <source>This help message</source> <translation>This help message</translation> </message> <message> - <location line="-118"/> + <location line="-108"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+68"/> + <location line="+59"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> <message> - <location line="-42"/> + <location line="-33"/> <source>Error loading wallet.dat: Wallet corrupted</source> <translation>Error loading wallet.dat: Wallet corrupted</translation> </message> @@ -4405,7 +4375,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+33"/> + <location line="+23"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> @@ -4415,7 +4385,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-122"/> + <location line="-112"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -4425,7 +4395,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+56"/> + <location line="+46"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> @@ -4440,22 +4410,22 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Insufficient funds</translation> </message> <message> - <location line="+13"/> + <location line="+14"/> <source>Loading block index...</source> <translation>Loading block index...</translation> </message> <message> - <location line="-70"/> + <location line="-61"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> <message> - <location line="+71"/> + <location line="+62"/> <source>Loading wallet...</source> <translation>Loading wallet...</translation> </message> <message> - <location line="-66"/> + <location line="-57"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -4465,22 +4435,22 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot write default address</translation> </message> <message> - <location line="+86"/> + <location line="+77"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> <message> - <location line="-73"/> + <location line="-64"/> <source>Done loading</source> <translation>Done loading</translation> </message> <message> - <location line="+101"/> + <location line="+91"/> <source>To use the %s option</source> <translation>To use the %s option</translation> </message> <message> - <location line="-93"/> + <location line="-83"/> <source>Error</source> <translation>Error</translation> </message> diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp new file mode 100644 index 0000000000..62c44703f4 --- /dev/null +++ b/src/qt/networkstyle.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "networkstyle.h" + +#include "guiconstants.h" + +#include <QApplication> + +static const struct { + const char *networkId; + const char *appName; + const char *appIcon; + const char *titleAddText; + const char *splashImage; +} network_styles[] = { + {"main", QAPP_APP_NAME_DEFAULT, ":/icons/bitcoin", "", ":/images/splash"}, + {"test", QAPP_APP_NAME_TESTNET, ":/icons/bitcoin_testnet", QT_TRANSLATE_NOOP("SplashScreen", "[testnet]"), ":/images/splash_testnet"}, + {"regtest", QAPP_APP_NAME_TESTNET, ":/icons/bitcoin_testnet", "[regtest]", ":/images/splash_testnet"} +}; +static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles); + +// titleAddText needs to be const char* for tr() +NetworkStyle::NetworkStyle(const QString &appName, const QString &appIcon, const char *titleAddText, const QString &splashImage): + appName(appName), + appIcon(appIcon), + titleAddText(qApp->translate("SplashScreen", titleAddText)), + splashImage(splashImage) +{ +} + +const NetworkStyle *NetworkStyle::instantiate(const QString &networkId) +{ + for (unsigned x=0; x<network_styles_count; ++x) + { + if (networkId == network_styles[x].networkId) + { + return new NetworkStyle( + network_styles[x].appName, + network_styles[x].appIcon, + network_styles[x].titleAddText, + network_styles[x].splashImage); + } + } + return 0; +} diff --git a/src/qt/networkstyle.h b/src/qt/networkstyle.h new file mode 100644 index 0000000000..99304d61a3 --- /dev/null +++ b/src/qt/networkstyle.h @@ -0,0 +1,33 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_NETWORKSTYLE +#define H_NETWORKSTYLE + +#include <QIcon> +#include <QPixmap> +#include <QString> + +/* Coin network-specific GUI style information */ +class NetworkStyle +{ +public: + /** Get style associated with provided BIP70 network id, or 0 if not known */ + static const NetworkStyle *instantiate(const QString &networkId); + + const QString &getAppName() const { return appName; } + const QIcon &getAppIcon() const { return appIcon; } + const QString &getTitleAddText() const { return titleAddText; } + const QPixmap &getSplashImage() const { return splashImage; } + +private: + NetworkStyle(const QString &appName, const QString &appIcon, const char *titleAddText, const QString &splashImage); + + QString appName; + QIcon appIcon; + QString titleAddText; + QPixmap splashImage; +}; + +#endif // H_NETWORKSTYLE diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index cb80bd0e3d..6db654dff7 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -119,6 +119,8 @@ void OptionsModel::Init() // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) addOverriddenOption("-proxy"); + else if(!settings.value("fUseProxy").toBool() && !GetArg("-proxy", "").empty()) + addOverriddenOption("-proxy"); // Display if (!settings.contains("language")) diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 4fe610794f..b4b81440ca 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -6,6 +6,7 @@ #include "clientversion.h" #include "init.h" +#include "networkstyle.h" #include "ui_interface.h" #include "util.h" #include "version.h" @@ -19,7 +20,7 @@ #include <QDesktopWidget> #include <QPainter> -SplashScreen::SplashScreen(Qt::WindowFlags f, bool isTestNet) : +SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) : QWidget(0, f), curAlignment(0) { // set reference point, paddings @@ -34,17 +35,12 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, bool isTestNet) : QString titleText = tr("Bitcoin Core"); QString versionText = QString("Version %1").arg(QString::fromStdString(FormatFullVersion())); QString copyrightText = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin Core developers")); - QString testnetAddText = QString(tr("[testnet]")); // define text to place as single text object + QString titleAddText = networkStyle->getTitleAddText(); QString font = "Arial"; // load the bitmap for writing some text over it - if(isTestNet) { - pixmap = QPixmap(":/images/splash_testnet"); - } - else { - pixmap = QPixmap(":/images/splash"); - } + pixmap = networkStyle->getSplashImage(); QPainter pixPaint(&pixmap); pixPaint.setPen(QColor(100,100,100)); @@ -78,23 +74,20 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, bool isTestNet) : pixPaint.setFont(QFont(font, 10*fontFactor)); pixPaint.drawText(pixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText); - // draw testnet string if testnet is on - if(isTestNet) { + // draw additional text if special network + if(!titleAddText.isEmpty()) { QFont boldFont = QFont(font, 10*fontFactor); boldFont.setWeight(QFont::Bold); pixPaint.setFont(boldFont); fm = pixPaint.fontMetrics(); - int testnetAddTextWidth = fm.width(testnetAddText); - pixPaint.drawText(pixmap.width()-testnetAddTextWidth-10,15,testnetAddText); + int titleAddTextWidth = fm.width(titleAddText); + pixPaint.drawText(pixmap.width()-titleAddTextWidth-10,15,titleAddText); } pixPaint.end(); // Set window title - if(isTestNet) - setWindowTitle(titleText + " " + testnetAddText); - else - setWindowTitle(titleText); + setWindowTitle(titleText + " " + titleAddText); // Resize window and move to center of desktop, disallow resizing QRect r(QPoint(), pixmap.size()); @@ -177,5 +170,6 @@ void SplashScreen::paintEvent(QPaintEvent *event) void SplashScreen::closeEvent(QCloseEvent *event) { + StartShutdown(); // allows an "emergency" shutdown during startup event->ignore(); } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 89c21e6457..128edadbee 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -7,6 +7,8 @@ #include <QSplashScreen> +class NetworkStyle; + /** Class for the splashscreen with information of the running client. * * @note this is intentionally not a QSplashScreen. Bitcoin Core initialization @@ -18,7 +20,7 @@ class SplashScreen : public QWidget Q_OBJECT public: - explicit SplashScreen(Qt::WindowFlags f, bool isTestNet); + explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle); ~SplashScreen(); protected: diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 4b3beae20c..24175215bf 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -381,7 +381,7 @@ Value gettxout(const Array& params, bool fHelp) CCoins coins; if (fMempool) { LOCK(mempool.cs); - CCoinsViewMemPool view(*pcoinsTip, mempool); + CCoinsViewMemPool view(pcoinsTip, mempool); if (!view.GetCoins(hash, coins)) return Value::null; mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index e794bf69e0..c767835a27 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -239,7 +239,7 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET)); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); obj.push_back(Pair("chain", Params().NetworkIDString())); #ifdef ENABLE_WALLET obj.push_back(Pair("generate", getgenerate(params, false))); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index dd45eefd58..8be14b567c 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -87,7 +87,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET)); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); #ifdef ENABLE_WALLET if (pwalletMain) { obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index fb159d96f6..bc19d1372a 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -368,23 +368,29 @@ Value getnetworkinfo(const Array& params, bool fHelp) "Returns an object containing various state info regarding P2P networking.\n" "\nResult:\n" "{\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"networks\": [ (array) information per network\n" - " \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n" - " \"limited\": xxx, (boolean) is the network limited using -onlynet?\n" - " \"reachable\": xxx, (boolean) is the network reachable?\n" - " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" - " },\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"networks\": [ (array) information per network\n" + " {\n" + " \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n" + " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" + " \"reachable\": true|false, (boolean) is the network reachable?\n" + " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" + " }\n" + " ,...\n" " ],\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in btc/kb\n" - " \"localaddresses\": [, (array) list of local addresses\n" - " \"address\": \"xxxx\", (string) network address\n" - " \"port\": xxx, (numeric) network port\n" - " \"score\": xxx (numeric) relative score\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in btc/kb\n" + " \"localaddresses\": [ (array) list of local addresses\n" + " {\n" + " \"address\": \"xxxx\", (string) network address\n" + " \"port\": xxx, (numeric) network port\n" + " \"score\": xxx (numeric) relative score\n" + " }\n" + " ,...\n" " ]\n" "}\n" "\nExamples:\n" @@ -393,10 +399,10 @@ Value getnetworkinfo(const Array& params, bool fHelp) ); Object obj; - obj.push_back(Pair("version", (int)CLIENT_VERSION)); + obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("subversion", FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()))); - obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); + obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); obj.push_back(Pair("timeoffset", GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index bd87d77704..78372da685 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -110,6 +110,9 @@ Value getrawtransaction(const Array& params, bool fHelp) if (fHelp || params.size() < 1 || 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" + "or there is an unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option.\n" "\nReturn the raw transaction data.\n" "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n" "If verbose is non-zero, returns an Object with information about 'txid'.\n" @@ -202,7 +205,7 @@ Value listunspent(const Array& params, bool fHelp) "Results are an array of Objects, each of which has:\n" "{txid, vout, scriptPubKey, amount, confirmations}\n" "\nArguments:\n" - "1. minconf (numeric, optional, default=1) The minimum confirmationsi to filter\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n" " [\n" @@ -557,11 +560,11 @@ Value signrawtransaction(const Array& params, bool fHelp) // Fetch previous transactions (inputs): CCoinsView viewDummy; - CCoinsViewCache view(viewDummy); + CCoinsViewCache view(&viewDummy); { LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; - CCoinsViewMemPool viewMempool(viewChain, mempool); + CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { @@ -612,21 +615,19 @@ Value signrawtransaction(const Array& params, bool fHelp) vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); - CCoins coins; - if (view.GetCoins(txid, coins)) { - if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) { + { + CCoinsModifier coins = view.ModifyCoins(txid); + if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { string err("Previous output scriptPubKey mismatch:\n"); - err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ + err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ scriptPubKey.ToString(); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } - // what todo if txid is known, but the actual output isn't? + if ((unsigned int)nOut >= coins->vout.size()) + coins->vout.resize(nOut+1); + coins->vout[nOut].scriptPubKey = scriptPubKey; + coins->vout[nOut].nValue = 0; // we don't know the actual output value } - if ((unsigned int)nOut >= coins.vout.size()) - coins.vout.resize(nOut+1); - coins.vout[nOut].scriptPubKey = scriptPubKey; - coins.vout[nOut].nValue = 0; // we don't know the actual output value - view.SetCoins(txid, coins); // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: @@ -687,7 +688,7 @@ Value signrawtransaction(const Array& params, bool fHelp) BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, STANDARD_SCRIPT_VERIFY_FLAGS)) + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(mergedTx, i))) fComplete = false; } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a71f55dd26..ae66217b7c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -9,14 +9,10 @@ #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha2.h" -#include "random.h" #include "script/script.h" #include "uint256.h" #include "util.h" -#include <boost/thread.hpp> -#include <boost/tuple/tuple_comparison.hpp> - using namespace std; typedef vector<unsigned char> valtype; @@ -56,10 +52,7 @@ static inline void popstack(vector<valtype>& stack) stack.pop_back(); } -bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { - if (!(flags & SCRIPT_VERIFY_STRICTENC)) - return true; - +bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { if (vchPubKey.size() < 33) return error("Non-canonical public key: too short"); if (vchPubKey[0] == 0x04) { @@ -74,10 +67,7 @@ bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { return true; } -bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { - if (!(flags & SCRIPT_VERIFY_STRICTENC)) - return true; - +bool static IsDERSignature(const valtype &vchSig) { // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype> // Where R and S are not negative (their first byte has its highest bit not set), and not @@ -87,9 +77,6 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return error("Non-canonical signature: too short"); if (vchSig.size() > 73) return error("Non-canonical signature: too long"); - unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); - if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) - return error("Non-canonical signature: unknown hashtype byte"); if (vchSig[0] != 0x30) return error("Non-canonical signature: wrong type"); if (vchSig[1] != vchSig.size()-3) @@ -121,18 +108,55 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) return error("Non-canonical signature: S value excessively padded"); - if (flags & SCRIPT_VERIFY_LOW_S) { - // If the S value is above the order of the curve divided by two, its - // complement modulo the order could have been used instead, which is - // one byte shorter when encoded correctly. - if (!CKey::CheckSignatureElement(S, nLenS, true)) - return error("Non-canonical signature: S value is unnecessarily high"); + return true; +} + +bool static IsLowDERSignature(const valtype &vchSig) { + if (!IsDERSignature(vchSig)) { + return false; } + unsigned int nLenR = vchSig[3]; + unsigned int nLenS = vchSig[5+nLenR]; + const unsigned char *S = &vchSig[6+nLenR]; + // If the S value is above the order of the curve divided by two, its + // complement modulo the order could have been used instead, which is + // one byte shorter when encoded correctly. + if (!CKey::CheckSignatureElement(S, nLenS, true)) + return error("Non-canonical signature: S value is unnecessarily high"); return true; } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags) +bool static IsDefinedHashtypeSignature(const valtype &vchSig) { + if (vchSig.size() == 0) { + return false; + } + unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); + if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) + return error("Non-canonical signature: unknown hashtype byte"); + + return true; +} + +bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags) { + if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(vchSig)) { + return false; + } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig)) { + return false; + } else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSig)) { + return false; + } + return true; +} + +bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags) { + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) { + return false; + } + return true; +} + +bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker) { CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); @@ -674,8 +698,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); - bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && - CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, flags); + if (!CheckSignatureEncoding(vchSig, flags)) { + return false; + } + + bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); popstack(stack); popstack(stack); @@ -734,9 +761,12 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co valtype& vchSig = stacktop(-isig); valtype& vchPubKey = stacktop(-ikey); + if (!CheckSignatureEncoding(vchSig, flags)) { + return false; + } + // Check signature - bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && - CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, flags); + bool fOk = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); if (fOk) { isig++; @@ -897,7 +927,7 @@ public: } // anon namespace -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) { if (nIn >= txTo.vin.size()) { LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); @@ -921,70 +951,19 @@ uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -// Valid signature cache, to avoid doing expensive ECDSA signature checking -// twice for every transaction (once when accepted into memory pool, and -// again when accepted into the block chain) -class CSignatureCache +bool SignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { -private: - // sigdata_type is (signature hash, signature, public key): - typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type; - std::set< sigdata_type> setValid; - boost::shared_mutex cs_sigcache; - -public: - bool - Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) - { - boost::shared_lock<boost::shared_mutex> lock(cs_sigcache); - - sigdata_type k(hash, vchSig, pubKey); - std::set<sigdata_type>::iterator mi = setValid.find(k); - if (mi != setValid.end()) - return true; - return false; - } - - void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) - { - // DoS prevention: limit cache size to less than 10MB - // (~200 bytes per cache entry times 50,000 entries) - // Since there are a maximum of 20,000 signature operations per block - // 50,000 is a reasonable default. - int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); - if (nMaxCacheSize <= 0) return; - - boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); - - while (static_cast<int64_t>(setValid.size()) > nMaxCacheSize) - { - // Evict a random entry. Random because that helps - // foil would-be DoS attackers who might try to pre-generate - // and re-use a set of valid signatures just-slightly-greater - // than our cache size. - uint256 randomHash = GetRandHash(); - std::vector<unsigned char> unused; - std::set<sigdata_type>::iterator it = - setValid.lower_bound(sigdata_type(randomHash, unused, unused)); - if (it == setValid.end()) - it = setValid.begin(); - setValid.erase(*it); - } - - sigdata_type k(hash, vchSig, pubKey); - setValid.insert(k); - } -}; + return pubkey.Verify(sighash, vchSig); +} -bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char>& vchPubKey, const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int flags) +bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vector<unsigned char>& vchPubKey, const CScript& scriptCode) const { - static CSignatureCache signatureCache; - CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) return false; // Hash type is one byte tacked on to the end of the signature + vector<unsigned char> vchSig(vchSigIn); if (vchSig.empty()) return false; int nHashType = vchSig.back(); @@ -992,26 +971,20 @@ bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char>& vchPubK uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); - if (signatureCache.Get(sighash, vchSig, pubkey)) - return true; - - if (!pubkey.Verify(sighash, vchSig)) + if (!VerifySignature(vchSig, pubkey, sighash)) return false; - if (!(flags & SCRIPT_VERIFY_NOCACHE)) - signatureCache.Set(sighash, vchSig, pubkey); - return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker) { vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, txTo, nIn, flags)) + if (!EvalScript(stack, scriptSig, flags, checker)) return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, flags)) + if (!EvalScript(stack, scriptPubKey, flags, checker)) return false; if (stack.empty()) return false; @@ -1034,7 +1007,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, txTo, nIn, flags)) + if (!EvalScript(stackCopy, pubKey2, flags, checker)) return false; if (stackCopy.empty()) return false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index adca2142ac..de5ce2ced1 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -10,6 +10,7 @@ #include <stdint.h> #include <string> +class CPubKey; class CScript; class CTransaction; class uint256; @@ -27,19 +28,54 @@ enum enum { SCRIPT_VERIFY_NONE = 0, - SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts - SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys - SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values (<n/2) in signatures (depends on STRICTENC) - SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it) - SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length -}; -bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey, unsigned int flags); -bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int flags); + // Evaluate P2SH subscripts (softfork safe, BIP16). + SCRIPT_VERIFY_P2SH = (1U << 0), + + // Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure. + // Passing a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) to checksig causes that pubkey to be + // skipped (not softfork safe: this flag can widen the validity of OP_CHECKSIG OP_NOT). + SCRIPT_VERIFY_STRICTENC = (1U << 1), + + // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1) + SCRIPT_VERIFY_DERSIG = (1U << 2), + + // Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure + // (softfork safe, BIP62 rule 5). + SCRIPT_VERIFY_LOW_S = (1U << 3), + + // verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7). + SCRIPT_VERIFY_NULLDUMMY = (1U << 4), +}; uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -bool CheckSig(std::vector<unsigned char> vchSig, const std::vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int flags); -bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags); + +class BaseSignatureChecker +{ +public: + virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const + { + return false; + } + + virtual ~BaseSignatureChecker() {} +}; + +class SignatureChecker : public BaseSignatureChecker +{ +private: + const CTransaction& txTo; + unsigned int nIn; + +protected: + virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; + +public: + SignatureChecker(const CTransaction& txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const; +}; + +bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker); #endif // H_BITCOIN_SCRIPT_INTERPRETER diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp new file mode 100644 index 0000000000..ab366898d8 --- /dev/null +++ b/src/script/sigcache.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sigcache.h" + +#include "key.h" +#include "random.h" +#include "uint256.h" +#include "util.h" + +#include <boost/thread.hpp> +#include <boost/tuple/tuple_comparison.hpp> + +namespace { + +// Valid signature cache, to avoid doing expensive ECDSA signature checking +// twice for every transaction (once when accepted into memory pool, and +// again when accepted into the block chain) +class CSignatureCache +{ +private: + // sigdata_type is (signature hash, signature, public key): + typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type; + std::set< sigdata_type> setValid; + boost::shared_mutex cs_sigcache; + +public: + bool + Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) + { + boost::shared_lock<boost::shared_mutex> lock(cs_sigcache); + + sigdata_type k(hash, vchSig, pubKey); + std::set<sigdata_type>::iterator mi = setValid.find(k); + if (mi != setValid.end()) + return true; + return false; + } + + void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) + { + // DoS prevention: limit cache size to less than 10MB + // (~200 bytes per cache entry times 50,000 entries) + // Since there are a maximum of 20,000 signature operations per block + // 50,000 is a reasonable default. + int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + if (nMaxCacheSize <= 0) return; + + boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); + + while (static_cast<int64_t>(setValid.size()) > nMaxCacheSize) + { + // Evict a random entry. Random because that helps + // foil would-be DoS attackers who might try to pre-generate + // and re-use a set of valid signatures just-slightly-greater + // than our cache size. + uint256 randomHash = GetRandHash(); + std::vector<unsigned char> unused; + std::set<sigdata_type>::iterator it = + setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + if (it == setValid.end()) + it = setValid.begin(); + setValid.erase(*it); + } + + sigdata_type k(hash, vchSig, pubKey); + setValid.insert(k); + } +}; + +} + +bool CachingSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +{ + static CSignatureCache signatureCache; + + if (signatureCache.Get(sighash, vchSig, pubkey)) + return true; + + if (!SignatureChecker::VerifySignature(vchSig, pubkey, sighash)) + return false; + + if (store) + signatureCache.Set(sighash, vchSig, pubkey); + return true; +} diff --git a/src/script/sigcache.h b/src/script/sigcache.h new file mode 100644 index 0000000000..46b8f4d335 --- /dev/null +++ b/src/script/sigcache.h @@ -0,0 +1,26 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPT_SIGCACHE +#define H_BITCOIN_SCRIPT_SIGCACHE + +#include "script/interpreter.h" + +#include <vector> + +class CPubKey; + +class CachingSignatureChecker : public SignatureChecker +{ +private: + bool store; + +public: + CachingSignatureChecker(const CTransaction& txToIn, unsigned int nInIn, bool storeIn=true) : SignatureChecker(txToIn, nInIn), store(storeIn) {} + + bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; +}; + +#endif // H_BITCOIN_SCRIPT_SIGCACHE diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8abd8d221d..da77e7d1f1 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -123,7 +123,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS); + return VerifyScript(txin.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(txTo, nIn)); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) @@ -174,7 +174,7 @@ static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0)) + if (SignatureChecker(txTo, nIn).CheckSig(sig, pubkey, scriptPubKey)) { sigs[pubkey] = sig; break; @@ -252,9 +252,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign Solver(scriptPubKey, txType, vSolutions); vector<valtype> stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC); + EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); vector<valtype> stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC); + EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 407baf621d..53ae254d59 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -203,7 +203,11 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) if (whichType == TX_PUBKEY) { - addressRet = CPubKey(vSolutions[0]).GetID(); + CPubKey pubKey(vSolutions[0]); + if (!pubKey.IsValid()) + return false; + + addressRet = pubKey.GetID(); return true; } else if (whichType == TX_PUBKEYHASH) @@ -237,9 +241,16 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto nRequiredRet = vSolutions.front()[0]; for (unsigned int i = 1; i < vSolutions.size()-1; i++) { - CTxDestination address = CPubKey(vSolutions[i]).GetID(); + CPubKey pubKey(vSolutions[i]); + if (!pubKey.IsValid()) + continue; + + CTxDestination address = pubKey.GetID(); addressRet.push_back(address); } + + if (addressRet.empty()) + return false; } else { diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp deleted file mode 100644 index a17099de72..0000000000 --- a/src/test/canonical_tests.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2012-2013 The Bitcoin Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -// -// Unit tests for canonical signatures -// - -#include "data/sig_noncanonical.json.h" -#include "data/sig_canonical.json.h" -#include "key.h" -#include "random.h" -#include "script/interpreter.h" -#include "util.h" -#include "utilstrencodings.h" - -#include <boost/foreach.hpp> -#include <boost/test/unit_test.hpp> -#include "json/json_spirit_writer_template.h" -#include <openssl/ecdsa.h> - -using namespace std; -using namespace json_spirit; - -// In script_tests.cpp -extern Array read_json(const std::string& jsondata); - -BOOST_AUTO_TEST_SUITE(canonical_tests) - -// OpenSSL-based test for canonical signature (without test for hashtype byte) -bool static IsCanonicalSignature_OpenSSL_inner(const std::vector<unsigned char>& vchSig) -{ - if (vchSig.size() == 0) - return false; - const unsigned char *input = &vchSig[0]; - ECDSA_SIG *psig = NULL; - d2i_ECDSA_SIG(&psig, &input, vchSig.size()); - if (psig == NULL) - return false; - unsigned char buf[256]; - unsigned char *pbuf = buf; - unsigned int nLen = i2d_ECDSA_SIG(psig, NULL); - if (nLen != vchSig.size()) { - ECDSA_SIG_free(psig); - return false; - } - nLen = i2d_ECDSA_SIG(psig, &pbuf); - ECDSA_SIG_free(psig); - return (memcmp(&vchSig[0], &buf[0], nLen) == 0); -} - -// OpenSSL-based test for canonical signature -bool static IsCanonicalSignature_OpenSSL(const std::vector<unsigned char> &vchSignature) { - if (vchSignature.size() < 1) - return false; - if (vchSignature.size() > 127) - return false; - if (vchSignature[vchSignature.size() - 1] & 0x7C) - return false; - - std::vector<unsigned char> vchSig(vchSignature); - vchSig.pop_back(); - if (!IsCanonicalSignature_OpenSSL_inner(vchSig)) - return false; - return true; -} - -BOOST_AUTO_TEST_CASE(script_canon) -{ - Array tests = read_json(std::string(json_tests::sig_canonical, json_tests::sig_canonical + sizeof(json_tests::sig_canonical))); - - BOOST_FOREACH(Value &tv, tests) { - string test = tv.get_str(); - if (IsHex(test)) { - std::vector<unsigned char> sig = ParseHex(test); - BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC), test); - BOOST_CHECK_MESSAGE(IsCanonicalSignature_OpenSSL(sig), test); - } - } -} - -BOOST_AUTO_TEST_CASE(script_noncanon) -{ - Array tests = read_json(std::string(json_tests::sig_noncanonical, json_tests::sig_noncanonical + sizeof(json_tests::sig_noncanonical))); - - BOOST_FOREACH(Value &tv, tests) { - string test = tv.get_str(); - if (IsHex(test)) { - std::vector<unsigned char> sig = ParseHex(test); - BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC), test); - BOOST_CHECK_MESSAGE(!IsCanonicalSignature_OpenSSL(sig), test); - } - } -} - -BOOST_AUTO_TEST_CASE(script_signstrict) -{ - for (int i=0; i<100; i++) { - CKey key; - key.MakeNewKey(i & 1); - std::vector<unsigned char> sig; - uint256 hash = GetRandHash(); - - BOOST_CHECK(key.Sign(hash, sig)); // Generate a random signature. - BOOST_CHECK(key.GetPubKey().Verify(hash, sig)); // Check it. - sig.push_back(0x01); // Append a sighash type. - - BOOST_CHECK(IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_LOW_S)); - BOOST_CHECK(IsCanonicalSignature_OpenSSL(sig)); - } -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp new file mode 100644 index 0000000000..3ecd301bc7 --- /dev/null +++ b/src/test/coins_tests.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 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. + +#include "coins.h" +#include "random.h" +#include "uint256.h" + +#include <vector> +#include <map> + +#include <boost/test/unit_test.hpp> + +namespace +{ +class CCoinsViewTest : public CCoinsView +{ + uint256 hashBestBlock_; + std::map<uint256, CCoins> map_; + +public: + bool GetCoins(const uint256& txid, CCoins& coins) const + { + std::map<uint256, CCoins>::const_iterator it = map_.find(txid); + if (it == map_.end()) { + return false; + } + coins = it->second; + if (coins.IsPruned() && insecure_rand() % 2 == 0) { + // Randomly return false in case of an empty entry. + return false; + } + return true; + } + + bool HaveCoins(const uint256& txid) const + { + CCoins coins; + return GetCoins(txid, coins); + } + + uint256 GetBestBlock() const { return hashBestBlock_; } + + bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) + { + for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) { + map_[it->first] = it->second.coins; + if (it->second.coins.IsPruned() && insecure_rand() % 3 == 0) { + // Randomly delete empty entries on write. + map_.erase(it->first); + } + mapCoins.erase(it++); + } + mapCoins.clear(); + hashBestBlock_ = hashBlock; + return true; + } + + bool GetStats(CCoinsStats& stats) const { return false; } +}; +} + +BOOST_AUTO_TEST_SUITE(coins_tests) + +static const unsigned int NUM_SIMULATION_ITERATIONS = 40000; + +// This is a large randomized insert/remove simulation test on a variable-size +// stack of caches on top of CCoinsViewTest. +// +// It will randomly create/update/delete CCoins entries to a tip of caches, with +// txids picked from a limited list of random 256-bit hashes. Occasionally, a +// new tip is added to the stack of caches, or the tip is flushed and removed. +// +// During the process, booleans are kept to make sure that the randomized +// operation hits all branches. +BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) +{ + // Various coverage trackers. + bool removed_all_caches = false; + bool reached_4_caches = false; + bool added_an_entry = false; + bool removed_an_entry = false; + bool updated_an_entry = false; + bool found_an_entry = false; + bool missed_an_entry = false; + + // A simple map to track what we expect the cache stack to represent. + std::map<uint256, CCoins> result; + + // The cache stack. + CCoinsViewTest base; // A CCoinsViewTest at the bottom. + std::vector<CCoinsViewCache*> stack; // A stack of CCoinsViewCaches on top. + stack.push_back(new CCoinsViewCache(&base)); // Start with one cache. + + // Use a limited set of random transaction ids, so we do test overwriting entries. + std::vector<uint256> txids; + txids.resize(NUM_SIMULATION_ITERATIONS / 8); + for (unsigned int i = 0; i < txids.size(); i++) { + txids[i] = GetRandHash(); + } + + for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) { + // Do a random modification. + { + uint256 txid = txids[insecure_rand() % txids.size()]; // txid we're going to modify in this iteration. + CCoins& coins = result[txid]; + CCoinsModifier entry = stack.back()->ModifyCoins(txid); + BOOST_CHECK(coins == *entry); + if (insecure_rand() % 5 == 0 || coins.IsPruned()) { + if (coins.IsPruned()) { + added_an_entry = true; + } else { + updated_an_entry = true; + } + coins.nVersion = insecure_rand(); + coins.vout.resize(1); + coins.vout[0].nValue = insecure_rand(); + *entry = coins; + } else { + coins.Clear(); + entry->Clear(); + removed_an_entry = true; + } + } + + // Once every 1000 iterations and at the end, verify the full cache. + if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { + for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) { + const CCoins* coins = stack.back()->AccessCoins(it->first); + if (coins) { + BOOST_CHECK(*coins == it->second); + found_an_entry = true; + } else { + BOOST_CHECK(it->second.IsPruned()); + missed_an_entry = true; + } + } + } + + if (insecure_rand() % 100 == 0) { + // Every 100 iterations, change the cache stack. + if (stack.size() > 0 && insecure_rand() % 2 == 0) { + stack.back()->Flush(); + delete stack.back(); + stack.pop_back(); + } + if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) { + CCoinsView* tip = &base; + if (stack.size() > 0) { + tip = stack.back(); + } else { + removed_all_caches = true; + } + stack.push_back(new CCoinsViewCache(tip)); + if (stack.size() == 4) { + reached_4_caches = true; + } + } + } + } + + // Clean up the stack. + while (stack.size() > 0) { + delete stack.back(); + stack.pop_back(); + } + + // Verify coverage. + BOOST_CHECK(removed_all_caches); + BOOST_CHECK(reached_4_caches); + BOOST_CHECK(added_an_entry); + BOOST_CHECK(removed_an_entry); + BOOST_CHECK(updated_an_entry); + BOOST_CHECK(found_an_entry); + BOOST_CHECK(missed_an_entry); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json index 35a6794b01..b6447cb221 100644 --- a/src/test/data/script_invalid.json +++ b/src/test/data/script_invalid.json @@ -384,19 +384,126 @@ nSequences are max. ["0x00", "'00' EQUAL", "P2SH,STRICTENC", "Basic OP_0 execution"], -["0x47 0x30440220304eff7556bba9560df47873275e64db45f3cd735998ce3f00d2e57b1bb5f31302205c0c9d14b8b80d43e2ac9b87532f1af6d8a3271262bc694ec4e14068392bb0a001", "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", "", "P2PK, bad sig"], -["0x47 0x3044022037fcdb8e08f41e27588de8bc036d2c4b16eb3d09c1ba53b8f47a0a9c27722a39022058664b7a53b507e71dfafb77193e3786c3f0c119d78ce9104480ee7ece04f09301 0x21 0x03363d90d446b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640", "DUP HASH160 0x14 0xc0834c0c158f53be706d234c38fd52de7eece656 EQUALVERIFY CHECKSIG", "", "P2PKH, bad pubkey"], -["0x47 0x3044022035e5b6742d299861c84cebaf2ea64145ee427a95facab39e2594d6deebb0c1d602200acb16778faa2e467a59006f342f2535b1418d55ba63a8605b387b7f9ac86d9a01", "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", "", "P2PK anyonecanpay marked with normal hashtype"], -["0x47 0x3044022029b2b8765ca950cf75a69e80b73b7ddfcaa8b27080c2db4c23b36aae60688e790220598ff368e17872ee065aa54d7d3a590682ca5204325b23b31d7da3c4a21ae67901 0x23 0x210279be667ef9dcbbac54a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", "P2SH", "P2SH(P2PK), bad redeemscript"], -["0x47 0x30440220647f906e63890df5ef1d3fed47ba892b31976c634281079e2bd38504fb54a1fb022021e8811f38fbe90efb6b74cb78da01d9badbac3bafdf70a861d7538a220d0b2601 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", "P2SH", "P2SH(P2PKH), bad sig"], -["0 0x47 0x304402203ef170402f8887f2ac183f31b1f503b0bc60bfc968dd469b097ea6124aefac5002200612febadc4e4cacc086982cb85830a17af3680c1b6a3cf77c1708af7621cf1301 0 0x47 0x304402207821838251a24a2234844f68e7169e6d11945cdf052ea12bd3e4e37457aceb4402200b6b46c81361e314c740ae5133c072af5fa5c209d65d2db1679e1716f19a538101", "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", "", "3-of-3, 2 sigs"], -["0 0 0x47 0x304402204661f7795e8db7be3132e8974e9a76d1d24b31f23df94c6fbcea07d1c205789102203f5e45a1c0b085279b58d11b36d5fea5449c3cf16f844ad10124e9b65e8777d201 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", "P2SH", "P2SH(2-of-3), 1 sig"], -["0x47 0x304402200052bc1600ca45c71f3538720fe62a5e8548dffd137af04467598c98466e9c0a0220789318ddbc9991ee477974089220a2feb6a6298a7c93d5ff6c25a92a2f4b48d501", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "STRICTENC", "P2PK with too much R padding"], -["0x48 0x304502206eb7b92628bfb3c4d2a04b65b986987bcbb1af4fceedb144d5a0437b7ee410590221005f57a52df4aa26366742eed0db182fce51fbcd7159011b0644a7c05943eb228901", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "STRICTENC", "P2PK with too much S padding"], -["0x47 0x30440220d8ad1efd55a3d2b8896495c38aba72056e1b3ca4a6ca15760e843eb1a9b9907602203eb0e8f3d6bec998262dfd03eaeb0f31c4e5105965436dec77550724b3771f3201", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "STRICTENC", "P2PK with too little R padding"], -["0x48 0x304502206c43e065c8a8db3bbe69015afb86a51fb2fc8870defd41d436da2a197d9d6c12022100fcec35816ee2d84ec271ad159fcabf5dd712157051169e48ac328a7818cdb51e01", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", "LOW_S,STRICTENC", "P2PK with high S"], -["0x01 0x01 0x47 0x304402200e48ba1cf4d7182db94ffb57bd72ea31b5545dc0d1c512e665779b4fb2badc52022054b8388dfc074c708a75b62359b7be46402751ee40c0a111aef38a837b6ed09801 0x47 0x304402201c9820f59c49107bb30e6175cfc9ec95f897b03beb628b4bc854d2b80392aa0602200235d986ae418bcd111b8814f4c26a0ab5f475fb542a44884fc14912a97a252301 0x47 0x304402204cd7894c6f10a871f5b0c1f9c13228f8cdd4050248f0d0f498ee86be69ee3080022051bd2932c7d585eb600c7194235c74da820935f0d67972fd9545673aa1fd023301", "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", "NULLDUMMY", "3-of-3 with nonzero dummy"], +[ + "0x47 0x30440220304eff7556bba9560df47873275e64db45f3cd735998ce3f00d2e57b1bb5f31302205c0c9d14b8b80d43e2ac9b87532f1af6d8a3271262bc694ec4e14068392bb0a001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "P2PK, bad sig" +], +[ + "0x47 0x3044022037fcdb8e08f41e27588de8bc036d2c4b16eb3d09c1ba53b8f47a0a9c27722a39022058664b7a53b507e71dfafb77193e3786c3f0c119d78ce9104480ee7ece04f09301 0x21 0x03363d90d446b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640", + "DUP HASH160 0x14 0xc0834c0c158f53be706d234c38fd52de7eece656 EQUALVERIFY CHECKSIG", + "", + "P2PKH, bad pubkey" +], +[ + "0x47 0x3044022035e5b6742d299861c84cebaf2ea64145ee427a95facab39e2594d6deebb0c1d602200acb16778faa2e467a59006f342f2535b1418d55ba63a8605b387b7f9ac86d9a01", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "P2PK anyonecanpay marked with normal hashtype" +], +[ + "0x47 0x3044022029b2b8765ca950cf75a69e80b73b7ddfcaa8b27080c2db4c23b36aae60688e790220598ff368e17872ee065aa54d7d3a590682ca5204325b23b31d7da3c4a21ae67901 0x23 0x210279be667ef9dcbbac54a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", + "P2SH", + "P2SH(P2PK), bad redeemscript" +], +[ + "0x47 0x30440220647f906e63890df5ef1d3fed47ba892b31976c634281079e2bd38504fb54a1fb022021e8811f38fbe90efb6b74cb78da01d9badbac3bafdf70a861d7538a220d0b2601 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", + "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", + "P2SH", + "P2SH(P2PKH), bad sig" +], +[ + "0 0x47 0x304402203ef170402f8887f2ac183f31b1f503b0bc60bfc968dd469b097ea6124aefac5002200612febadc4e4cacc086982cb85830a17af3680c1b6a3cf77c1708af7621cf1301 0 0x47 0x304402207821838251a24a2234844f68e7169e6d11945cdf052ea12bd3e4e37457aceb4402200b6b46c81361e314c740ae5133c072af5fa5c209d65d2db1679e1716f19a538101", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "3-of-3, 2 sigs" +], +[ + "0 0 0x47 0x304402204661f7795e8db7be3132e8974e9a76d1d24b31f23df94c6fbcea07d1c205789102203f5e45a1c0b085279b58d11b36d5fea5449c3cf16f844ad10124e9b65e8777d201 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", + "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", + "P2SH", + "P2SH(2-of-3), 1 sig" +], +[ + "0x47 0x304402200052bc1600ca45c71f3538720fe62a5e8548dffd137af04467598c98466e9c0a0220789318ddbc9991ee477974089220a2feb6a6298a7c93d5ff6c25a92a2f4b48d501", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "STRICTENC", + "P2PK with too much R padding" +], +[ + "0x48 0x304502206eb7b92628bfb3c4d2a04b65b986987bcbb1af4fceedb144d5a0437b7ee410590221005f57a52df4aa26366742eed0db182fce51fbcd7159011b0644a7c05943eb228901", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "STRICTENC", + "P2PK with too much S padding" +], +[ + "0x47 0x30440220d8ad1efd55a3d2b8896495c38aba72056e1b3ca4a6ca15760e843eb1a9b9907602203eb0e8f3d6bec998262dfd03eaeb0f31c4e5105965436dec77550724b3771f3201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "STRICTENC", + "P2PK with too little R padding" +], +[ + "0x47 0x30440220001d0f82c127470cb38316c96b1719b33382353687a1146a776dee8259606905022062cd1fc8eacef819d68f0f41cc9ae9fdc2e29b70c3c7ad2c6c18f39b4e35c42701", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with bad sig with too much R padding" +], +[ + "0x47 0x30440220005d727e2a82d6e8a98a6da6fbc281325644d1a40455e386fdb17883a8e6bc4d02202d15cca42ce136047a980d288e60c679d7e84cce18c3ceffb6bc81b9e9ba517801", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "P2PK NOT with too much R padding but no DERSIG" +], +[ + "0x47 0x30440220006e8bc4f82032b12bd594847c16d8b2986de734aa3b0528bd89d664d41e6d1c02200cfd582694891bcfa2e630e899bda257486eba00a007222fae71144dba07dc2901", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "P2PK NOT with too much R padding" +], +[ + "0x48 0x304502206c43e065c8a8db3bbe69015afb86a51fb2fc8870defd41d436da2a197d9d6c12022100fcec35816ee2d84ec271ad159fcabf5dd712157051169e48ac328a7818cdb51e01", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "LOW_S,STRICTENC", + "P2PK with high S" +], +[ + "0x47 0x304402203aab50cd7c30cc1e1475dee615b295bcee6ccf8aa8a7f6cda6b696c70d79cbb40220558e43fe7596c31146e2d077698d5a9c38351d8ba567549a2ae43ca97231c39501", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "STRICTENC", + "P2PK with hybrid pubkey" +], +[ + "0x47 0x304402205745e8f846110c185ee1185c01843a108588b81463d2c34d4a3f2445529f12fe02206ee6a2657bbc4e2bb74bfc44c3a5c4f410ed6356ca68982465de6ca807c807c201", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "", + "P2PK NOT with hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x304402201f82b99a813c9c48c8dee8d2c43b8f637b72353fe9bdcc084537bc17e2ab770402200c43b96a5f7e115f0114eabda32e068145965cb6c7b5ef64833bb4fcf9fc1b3b05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "STRICTENC", + "P2PK with undefined hashtype" +], +[ + "0x47 0x30440220166848cd5b82a32b5944d90de3c35249354b43773c2ece1844ee8d1103e2f6c602203b6b046da4243c77adef80ada9201b27bbfdf7f9d5428f40434b060432afd62005", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG NOT", + "STRICTENC", + "P2PK NOT with invalid sig and undefined hashtype" +], +[ + "0x01 0x01 0x47 0x304402200e48ba1cf4d7182db94ffb57bd72ea31b5545dc0d1c512e665779b4fb2badc52022054b8388dfc074c708a75b62359b7be46402751ee40c0a111aef38a837b6ed09801 0x47 0x304402201c9820f59c49107bb30e6175cfc9ec95f897b03beb628b4bc854d2b80392aa0602200235d986ae418bcd111b8814f4c26a0ab5f475fb542a44884fc14912a97a252301 0x47 0x304402204cd7894c6f10a871f5b0c1f9c13228f8cdd4050248f0d0f498ee86be69ee3080022051bd2932c7d585eb600c7194235c74da820935f0d67972fd9545673aa1fd023301", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "NULLDUMMY", + "3-of-3 with nonzero dummy" +], +[ + "0x01 0x01 0x47 0x304402201847fc3b8f7597768e7f543c58da1fca6e8e35eb28979431e6b637572ce6eaa4022048dd58608e040841d0bf52a70cfb70e1a9c8d2826fad068f4e9d2bf5c87766a501 0x47 0x30440220711311a72516affed73363763983d05c3d6a06a2eadf5d76b90b4354162ba94302204841a69e5955a7dc8e4ab3105fd0c86040c1dac6016297a51ddbf5079c28756801 0x47 0x30440220267e331a378191e7282fd10d61c97bf74bc97c233c5833d677936424ac08dee502201eee83d88b91988e1c4d9b979df2404aa190e0987a8ca09c4e5cd61da1d48ecc01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT", + "NULLDUMMY", + "3-of-3 NOT with invalid sig with nonzero dummy" +], ["The End"] - ] diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 653f60d982..88bec7238c 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -529,19 +529,126 @@ nSequences are max. ["0x00", "SIZE 0 EQUAL", "P2SH,STRICTENC", "Basic OP_0 execution"], -["0x47 0x3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501", "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", "", "P2PK"], -["0x47 0x3044022069d40999786aeb2fd874f9eb2636461a062dc963471627ed8390a3a5f9556f640220350132a52415ce622f2aadd07f791c591500917ec1f8c5edbc5381ef7942534d01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG", "", "P2PKH"], -["0x47 0x30440220519f2a6632ffa134c7811ea2819e9dcc951f0c7baf461f2dffdd09133f3b080a02203ec6bab5eb6619ed7f41b8701d7c6d70cfc83bb26c5c97f54b2ca6e304fc2bb581", "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", "", "P2PK anyonecanpay"], -["0x47 0x30440220279dad2170ffb5639f0a1ea71fc462ee37d75d420d86f84c978bac523c09b7f20220683b2789f5c5528a9e0a0d78f6e40db3f616cf1adb5a5fdef117d5974795cfe201 0x23 0x210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", "P2SH", "P2SH(P2PK)"], -["0x47 0x3044022066acbfb5ac96b7cbf3f05a2aaf358c32438c45d1d7359dee9fc1ee636940735f02205606a03fd8cbf6a6fcbcba60c8abb1e385c0b5753cb57a97538159106fd3684e01 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", "", "P2SH(P2PKH), bad sig but no VERIFY_P2SH"], -["0 0x47 0x3044022004e791dd30a64c70e55e84e150c002af9feb3ce0ab1f20e86c53d1209003927502205a60453987fcd72aebaaacebc8ce4b15449cdd79e54cc82cefb83e69dbcfeabf01 0x47 0x304402201d021808ce93dd8574cc4f99ae4f11b44305528b0aecbd9f156f08315173643802200944a0ea5c884bd86180aef76d8b1e444860776b251e47d2d6c651a1c6f9930801 0x47 0x30440220446336d7b7de05ebb5683b82b05248ec7d78e88ae8d6125985f5776c887a4cf90220674ab2b2c2f954ba1cf35457d273c90d0c0c1c224d0ae128628740e81129486801", "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", "", "3-of-3"], -["0 0x47 0x30440220288b06d057cf0eac434ed0c3be9257cc0ca144dd99c11cc8f1a49467a37d8e8002203c496c72253c528e6bc81c42e683aba974d46041a96ef7b00915c863eb2a702901 0x47 0x304402207ffb4da33f40cac839a43000a187bd76a1ee5bf95e46dc1534b38bb7bd0321db022038c078f29d1831f8eb68ffdc2634c654fb01c3467b6457b98ad220653bb2478501 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", "P2SH", "P2SH(2-of-3)"], -["0x47 0x304402200001cae94b795baaafb05db38cf24cd75560cab2c36c91e29fac7d0fd2a723a3022058e2e56e568ce7c4b2b106210d114e1faa079407a6ed4154f230667c7d3583bc01", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "", "P2PK with too much R padding but no STRICTENC"], -["0x48 0x304502206d01de7c2a40ac2bb1231ed97f3890a1782f421d4c28b97166deff317990288f0221005e720213b089355be2cf785d81a82c59307d30e1624f450ed9ca1ebbc11cca6d01", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "", "P2PK with too much S padding but no STRICTENC"], -["0x47 0x30440220f3d8889602147d60d26c1d3b21b8db183eac02bf6d2fec1424c0ef377ca6fd7b02202bae8bfe39d00a432d4538a592e338b0ffc44c17d4b7056043d55063cf91f5ef01", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "", "P2PK with too little R padding but no STRICTENC"], -["0x48 0x3045022021bf9184d94f208ac9f4757ebca9b1cbebf008cfc244fe5be1360b1b9aba0e92022100e55074f72f3a1bfddf2ea4ea7ba984f78822e136fe04c8f9c1363238e0233bd801", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", "STRICTENC", "P2PK with high S but no LOW_S"], -["0x48 0x304502205c3e81aaf2aad0673f349035b180eba783eba7797af91c979920dea6b17a16d6022100d1d46825c68da1b325f320a3503dad27bb818227f64a38d153554bfd360c0e5301", "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", "LOW_S", "P2PK with high S but no STRICTENC"], -["0x01 0x01 0x47 0x3044022046ce33d1771b0127dd4c4cef8fdc3218ebdfa60e3793ed700292d8ebd93fb1f402201029d47a414db83e96e31443c2d8b552f971469c4800f5eff7df2f0648521aed01 0x47 0x304402205c53911ad55b054920043962bbda98cf6e57e2db1cd5611138251490baabaa8702201dc80dfceae6007e7772dc13ff6e7ca66a983cb017fe5d46d30118462d83bcf801 0x47 0x304402201937e44a4ec12364f9d32f9d25e7ecbc68aee9ef90069af80efef4c05f6ace9602206c515101c00c75710b32ff7ff8dbaf7c9a0be6e86ed14a0755b47626604f31fd01", "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", "", "3-of-3 with nonzero dummy but no NULLDUMMY"], +[ + "0x47 0x3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "P2PK" +], +[ + "0x47 0x3044022069d40999786aeb2fd874f9eb2636461a062dc963471627ed8390a3a5f9556f640220350132a52415ce622f2aadd07f791c591500917ec1f8c5edbc5381ef7942534d01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG", + "", + "P2PKH" +], +[ + "0x47 0x30440220519f2a6632ffa134c7811ea2819e9dcc951f0c7baf461f2dffdd09133f3b080a02203ec6bab5eb6619ed7f41b8701d7c6d70cfc83bb26c5c97f54b2ca6e304fc2bb581", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "P2PK anyonecanpay" +], +[ + "0x47 0x30440220279dad2170ffb5639f0a1ea71fc462ee37d75d420d86f84c978bac523c09b7f20220683b2789f5c5528a9e0a0d78f6e40db3f616cf1adb5a5fdef117d5974795cfe201 0x23 0x210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", + "P2SH", + "P2SH(P2PK)" +], +[ + "0x47 0x3044022066acbfb5ac96b7cbf3f05a2aaf358c32438c45d1d7359dee9fc1ee636940735f02205606a03fd8cbf6a6fcbcba60c8abb1e385c0b5753cb57a97538159106fd3684e01 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", + "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", + "", + "P2SH(P2PKH), bad sig but no VERIFY_P2SH" +], +[ + "0 0x47 0x3044022004e791dd30a64c70e55e84e150c002af9feb3ce0ab1f20e86c53d1209003927502205a60453987fcd72aebaaacebc8ce4b15449cdd79e54cc82cefb83e69dbcfeabf01 0x47 0x304402201d021808ce93dd8574cc4f99ae4f11b44305528b0aecbd9f156f08315173643802200944a0ea5c884bd86180aef76d8b1e444860776b251e47d2d6c651a1c6f9930801 0x47 0x30440220446336d7b7de05ebb5683b82b05248ec7d78e88ae8d6125985f5776c887a4cf90220674ab2b2c2f954ba1cf35457d273c90d0c0c1c224d0ae128628740e81129486801", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "3-of-3" +], +[ + "0 0x47 0x30440220288b06d057cf0eac434ed0c3be9257cc0ca144dd99c11cc8f1a49467a37d8e8002203c496c72253c528e6bc81c42e683aba974d46041a96ef7b00915c863eb2a702901 0x47 0x304402207ffb4da33f40cac839a43000a187bd76a1ee5bf95e46dc1534b38bb7bd0321db022038c078f29d1831f8eb68ffdc2634c654fb01c3467b6457b98ad220653bb2478501 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", + "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", + "P2SH", + "P2SH(2-of-3)" +], +[ + "0x47 0x30440220001fff8863c84c0efc8eea5bffb7f388313f966f23a00ad3c0acc30ff5339684022016e6d78f51a3a1c362745931ca40b24f71cba2903dbfe5a6d392a9189127d83701", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "P2PK with too much R padding but no DERSIG" +], +[ + "0x48 0x304502202323d56f293842b544cacedd06baafb999196dfa1c2975314848c158ac606655022100514bd98186b8a3a1cc87f4aff76aed797781389f13f50d87bf95b2df6e488fcc01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "P2PK with too much S padding but no DERSIG" +], +[ + "0x47 0x30440220d31c24bb6c08a496e7698a08fd41975115d7b55bfaa31cb2d573e09481e59a6702206a691239996434076b78a4e1cf46fc8e993b468a9c77fb1832186aa8040a61a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "P2PK with too little R padding but no DERSIG" +], +[ + "0x47 0x30440220007c2cc7aef1801c2937447703c87ef2a3744209ad98da2abadd4ba8bb2e3ea00220503a275582c9f9e9ff30260c81b7f64b8b696f22105605cc8241fb76a797316201", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "P2PK NOT with bad sig with too much R padding but no DERSIG" +], +[ + "0x48 0x3045022021bf9184d94f208ac9f4757ebca9b1cbebf008cfc244fe5be1360b1b9aba0e92022100e55074f72f3a1bfddf2ea4ea7ba984f78822e136fe04c8f9c1363238e0233bd801", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "STRICTENC", + "P2PK with high S but no LOW_S" +], +[ + "0x47 0x304402202163bc732c21b7de0251297d3c6c2ece182782e85fc5e19d6036f1130a79051e022033827811634924ebba68767537d78dd7bd9109ae2a89a60587927abdc25eb06401", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "P2PK with hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x3044022078033e4227aa05ded69d8da579966578e230d8a7fb44d5f1a0620c3853c24f78022006a2e3f4d872ac8dfdc529110aa37301d65a76255a4b6cce2992adacd4d2c4e201", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "STRICTENC", + "P2PK NOT with hybrid pubkey" +], +[ + "0x47 0x3044022078d6c447887e88dcbe1bc5b613645280df6f4e5935648bc226e9d91da71b3216022047d6b7ef0949b228fc1b359afb8d50500268711354298217b983c26970790c7601", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "", + "P2PK NOT with invalid hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x304402207592427de20e315d644839754f2a5cca5b978b983a15e6da82109ede01722baa022032ceaf78590faa3f7743821e1b47b897ed1a57f6ee1c8a7519d23774d8de3c4401", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "STRICTENC", + "P2PK NOT with invalid hybrid pubkey" +], +[ + "0x47 0x304402204649e9517ef0377a8f8270bd423053fd98ddff62d74ea553e9579558abbb75e4022044a2b2344469c12e35ed898987711272b634733dd0f5e051288eceb04bd4669e05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "P2PK with undefined hashtype but no STRICTENC" +], +[ + "0x47 0x304402207f1cf1866a2df0bb4b8d84d0ade72aa3abb6aaab0639d608b23d9e10ead0c48202203caa97f22c3439443eea4b89f7f6729854df0f567a8184d6ecc6e8b6c68c3e9d05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG NOT", + "", + "P2PK NOT with invalid sig and undefined hashtype but no STRICTENC" +], +[ + "0x01 0x01 0x47 0x3044022046ce33d1771b0127dd4c4cef8fdc3218ebdfa60e3793ed700292d8ebd93fb1f402201029d47a414db83e96e31443c2d8b552f971469c4800f5eff7df2f0648521aed01 0x47 0x304402205c53911ad55b054920043962bbda98cf6e57e2db1cd5611138251490baabaa8702201dc80dfceae6007e7772dc13ff6e7ca66a983cb017fe5d46d30118462d83bcf801 0x47 0x304402201937e44a4ec12364f9d32f9d25e7ecbc68aee9ef90069af80efef4c05f6ace9602206c515101c00c75710b32ff7ff8dbaf7c9a0be6e86ed14a0755b47626604f31fd01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "3-of-3 with nonzero dummy but no NULLDUMMY" +], +[ + "0x01 0x01 0x47 0x30440220195038dbc6b2ae1199f86a6777824f7c5149789d85f655a3534a4422b8fba38c02204df9db87d2eb9fe06edc66870d9ac4c9ce673459f9d43cee0347ce4ffb02ee5a01 0x47 0x3044022010a45f30c6fa97a186eba9e6b595ab87d3dfcbf05dcaf1f1b8e3e7bf39515bb802203474e78d3d372e5f5c0f8c257ce8300c4bb8f37c51d4a894e11a91b5817da6ed01 0x47 0x30440220039cffd8e39850f95112662b1220b14b3c0d3d8a2772e13c947bfbf96345a64e02204154bfa77e2c0134d5434353bed82141e5da1cc479954aa288d5f0671480a04b01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT", + "", + "3-of-3 NOT with invalid sig and nonzero dummy but no NULLDUMMY" +], ["The End"] ] diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index cb37740068..5a2ec1cb31 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -82,19 +82,19 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.clear(); keys += key[0],key[1]; // magic operator+= from boost.assign s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, flags)); + BOOST_CHECK(VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0))); for (int i = 0; i < 4; i++) { keys.clear(); keys += key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0)), strprintf("a&b 1: %d", i)); keys.clear(); keys += key[1],key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, flags), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0)), strprintf("a&b 2: %d", i)); } // Test a OR b: @@ -104,16 +104,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i]; s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, flags), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0)), strprintf("a|b: %d", i)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, flags), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0)), strprintf("a|b: %d", i)); } s.clear(); s << OP_0 << OP_0; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags)); + BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0))); s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, flags)); + BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0))); for (int i = 0; i < 4; i++) @@ -123,9 +123,9 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i],key[j]; s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, flags), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0)), strprintf("escrow 1: %d %d", i, j)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, flags), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0)), strprintf("escrow 2: %d %d", i, j)); } } diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index e6cf00c2d0..f8361a0dc8 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -42,7 +42,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, txTo, 0, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, SignatureChecker(txTo, 0)); } @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)(); + bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)(); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) { LOCK(cs_main); CCoinsView coinsDummy; - CCoinsViewCache coins(coinsDummy); + CCoinsViewCache coins(&coinsDummy); CBasicKeyStore keystore; CKey key[6]; vector<CPubKey> keys; @@ -312,8 +312,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txFrom.vout[6].scriptPubKey = GetScriptForDestination(twentySigops.GetID()); txFrom.vout[6].nValue = 6000; - - coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); + coins.ModifyCoins(txFrom.GetHash())->FromTx(txFrom, 0); CMutableTransaction txTo; txTo.vout.resize(1); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 48a5635d8f..a4b0212494 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -34,6 +34,9 @@ using namespace std; using namespace json_spirit; using namespace boost::algorithm; +// Uncomment if you want to output updated JSON tests. +// #define UPDATE_JSON_TESTS + static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; unsigned int ParseScriptFlags(string strFlags); @@ -89,7 +92,7 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMu void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bool expect, const std::string& message) { - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)), 0, flags) == expect, message); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, SignatureChecker(BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)), 0)) == expect, message); } namespace @@ -239,14 +242,15 @@ public: return *this; } - operator std::string() + Array GetJSON() { DoPush(); - return "[\"" + - FormatScript(spendTx.vin[0].scriptSig) + "\", \"" + - FormatScript(creditTx.vout[0].scriptPubKey) + "\", \"" + - FormatScriptFlags(flags) + "\", \"" + - comment + "\"],\n"; + Array array; + array.push_back(FormatScript(spendTx.vin[0].scriptSig)); + array.push_back(FormatScript(creditTx.vout[0].scriptPubKey)); + array.push_back(FormatScriptFlags(flags)); + array.push_back(comment); + return array; } std::string GetComment() @@ -316,40 +320,87 @@ BOOST_AUTO_TEST_CASE(script_build) ).Num(0).PushSig(keys.key1).Num(0).PushRedeem()); good.push_back(TestBuilder(CScript() << keys.pubkey1C << OP_CHECKSIG, - "P2PK with too much R padding but no STRICTENC", 0 + "P2PK with too much R padding but no DERSIG", 0 ).PushSig(keys.key1, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); bad.push_back(TestBuilder(CScript() << keys.pubkey1C << OP_CHECKSIG, - "P2PK with too much R padding", SCRIPT_VERIFY_STRICTENC + "P2PK with too much R padding", SCRIPT_VERIFY_DERSIG ).PushSig(keys.key1, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); good.push_back(TestBuilder(CScript() << keys.pubkey1C << OP_CHECKSIG, - "P2PK with too much S padding but no STRICTENC", 0 + "P2PK with too much S padding but no DERSIG", 0 ).PushSig(keys.key1, SIGHASH_ALL).EditPush(1, "44", "45").EditPush(37, "20", "2100")); bad.push_back(TestBuilder(CScript() << keys.pubkey1C << OP_CHECKSIG, - "P2PK with too much S padding", SCRIPT_VERIFY_STRICTENC + "P2PK with too much S padding", SCRIPT_VERIFY_DERSIG ).PushSig(keys.key1, SIGHASH_ALL).EditPush(1, "44", "45").EditPush(37, "20", "2100")); good.push_back(TestBuilder(CScript() << keys.pubkey1C << OP_CHECKSIG, - "P2PK with too little R padding but no STRICTENC", 0 + "P2PK with too little R padding but no DERSIG", 0 ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); bad.push_back(TestBuilder(CScript() << keys.pubkey1C << OP_CHECKSIG, - "P2PK with too little R padding", SCRIPT_VERIFY_STRICTENC + "P2PK with too little R padding", SCRIPT_VERIFY_DERSIG ).PushSig(keys.key1, SIGHASH_ALL, 33, 32).EditPush(1, "45022100", "440220")); + good.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG << OP_NOT, + "P2PK NOT with bad sig with too much R padding but no DERSIG", 0 + ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000").DamagePush(10)); + bad.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG << OP_NOT, + "P2PK NOT with bad sig with too much R padding", SCRIPT_VERIFY_DERSIG + ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000").DamagePush(10)); + bad.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG << OP_NOT, + "P2PK NOT with too much R padding but no DERSIG", 0 + ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); + bad.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG << OP_NOT, + "P2PK NOT with too much R padding", SCRIPT_VERIFY_DERSIG + ).PushSig(keys.key2, SIGHASH_ALL, 31, 32).EditPush(1, "43021F", "44022000")); good.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG, - "P2PK with high S but no LOW_S", SCRIPT_VERIFY_STRICTENC - ).PushSig(keys.key2, SIGHASH_ALL, 32, 33)); - good.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG, - "P2PK with high S but no STRICTENC", SCRIPT_VERIFY_LOW_S + "P2PK with high S but no LOW_S", 0 ).PushSig(keys.key2, SIGHASH_ALL, 32, 33)); bad.push_back(TestBuilder(CScript() << keys.pubkey2C << OP_CHECKSIG, - "P2PK with high S", SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC + "P2PK with high S", SCRIPT_VERIFY_LOW_S ).PushSig(keys.key2, SIGHASH_ALL, 32, 33)); + good.push_back(TestBuilder(CScript() << keys.pubkey0H << OP_CHECKSIG, + "P2PK with hybrid pubkey but no STRICTENC", 0 + ).PushSig(keys.key0, SIGHASH_ALL)); + bad.push_back(TestBuilder(CScript() << keys.pubkey0H << OP_CHECKSIG, + "P2PK with hybrid pubkey", SCRIPT_VERIFY_STRICTENC + ).PushSig(keys.key0, SIGHASH_ALL)); + bad.push_back(TestBuilder(CScript() << keys.pubkey0H << OP_CHECKSIG << OP_NOT, + "P2PK NOT with hybrid pubkey but no STRICTENC", 0 + ).PushSig(keys.key0, SIGHASH_ALL)); + good.push_back(TestBuilder(CScript() << keys.pubkey0H << OP_CHECKSIG << OP_NOT, + "P2PK NOT with hybrid pubkey", SCRIPT_VERIFY_STRICTENC + ).PushSig(keys.key0, SIGHASH_ALL)); + good.push_back(TestBuilder(CScript() << keys.pubkey0H << OP_CHECKSIG << OP_NOT, + "P2PK NOT with invalid hybrid pubkey but no STRICTENC", 0 + ).PushSig(keys.key0, SIGHASH_ALL).DamagePush(10)); + good.push_back(TestBuilder(CScript() << keys.pubkey0H << OP_CHECKSIG << OP_NOT, + "P2PK NOT with invalid hybrid pubkey", SCRIPT_VERIFY_STRICTENC + ).PushSig(keys.key0, SIGHASH_ALL).DamagePush(10)); + + good.push_back(TestBuilder(CScript() << keys.pubkey1 << OP_CHECKSIG, + "P2PK with undefined hashtype but no STRICTENC", 0 + ).PushSig(keys.key1, 5)); + bad.push_back(TestBuilder(CScript() << keys.pubkey1 << OP_CHECKSIG, + "P2PK with undefined hashtype", SCRIPT_VERIFY_STRICTENC + ).PushSig(keys.key1, 5)); + good.push_back(TestBuilder(CScript() << keys.pubkey1 << OP_CHECKSIG << OP_NOT, + "P2PK NOT with invalid sig and undefined hashtype but no STRICTENC", 0 + ).PushSig(keys.key1, 5).DamagePush(10)); + bad.push_back(TestBuilder(CScript() << keys.pubkey1 << OP_CHECKSIG << OP_NOT, + "P2PK NOT with invalid sig and undefined hashtype", SCRIPT_VERIFY_STRICTENC + ).PushSig(keys.key1, 5).DamagePush(10)); + good.push_back(TestBuilder(CScript() << OP_3 << keys.pubkey0C << keys.pubkey1C << keys.pubkey2C << OP_3 << OP_CHECKMULTISIG, "3-of-3 with nonzero dummy but no NULLDUMMY", 0 ).Num(1).PushSig(keys.key0).PushSig(keys.key1).PushSig(keys.key2)); bad.push_back(TestBuilder(CScript() << OP_3 << keys.pubkey0C << keys.pubkey1C << keys.pubkey2C << OP_3 << OP_CHECKMULTISIG, "3-of-3 with nonzero dummy", SCRIPT_VERIFY_NULLDUMMY ).Num(1).PushSig(keys.key0).PushSig(keys.key1).PushSig(keys.key2)); + good.push_back(TestBuilder(CScript() << OP_3 << keys.pubkey0C << keys.pubkey1C << keys.pubkey2C << OP_3 << OP_CHECKMULTISIG << OP_NOT, + "3-of-3 NOT with invalid sig and nonzero dummy but no NULLDUMMY", 0 + ).Num(1).PushSig(keys.key0).PushSig(keys.key1).PushSig(keys.key2).DamagePush(10)); + bad.push_back(TestBuilder(CScript() << OP_3 << keys.pubkey0C << keys.pubkey1C << keys.pubkey2C << OP_3 << OP_CHECKMULTISIG << OP_NOT, + "3-of-3 NOT with invalid sig with nonzero dummy", SCRIPT_VERIFY_NULLDUMMY + ).Num(1).PushSig(keys.key0).PushSig(keys.key1).PushSig(keys.key2).DamagePush(10)); std::map<std::string, Array> tests_good; std::map<std::string, Array> tests_bad; @@ -377,18 +428,30 @@ BOOST_AUTO_TEST_CASE(script_build) BOOST_FOREACH(TestBuilder& test, good) { test.Test(true); - BOOST_CHECK_MESSAGE(tests_good.count(test.GetComment()) > 0, "Missing auto script_valid test: " + test.GetComment()); - BOOST_CHECK_MESSAGE(ParseScript(tests_good[test.GetComment()][1].get_str()) == test.GetScriptPubKey(), "ScriptPubKey mismatch in auto script_valid test: " + test.GetComment()); - strGood += test; + if (tests_good.count(test.GetComment()) == 0) { +#ifndef UPDATE_JSON_TESTS + BOOST_CHECK_MESSAGE(false, "Missing auto script_valid test: " + test.GetComment()); +#endif + strGood += write_string(Value(test.GetJSON()), true) + ",\n"; + } else { + BOOST_CHECK_MESSAGE(ParseScript(tests_good[test.GetComment()][1].get_str()) == test.GetScriptPubKey(), "ScriptPubKey mismatch in auto script_valid test: " + test.GetComment()); + strGood += write_string(Value(tests_good[test.GetComment()]), true) + ",\n"; + } } BOOST_FOREACH(TestBuilder& test, bad) { test.Test(false); - BOOST_CHECK_MESSAGE(tests_bad.count(test.GetComment()) > 0, "Missing auto script_invalid test: " + test.GetComment()); - BOOST_CHECK_MESSAGE(ParseScript(tests_bad[test.GetComment()][1].get_str()) == test.GetScriptPubKey(), "ScriptPubKey mismatch in auto script_invalid test: " + test.GetComment()); - strBad += test; + if (tests_bad.count(test.GetComment()) == 0) { +#ifndef UPDATE_JSON_TESTS + BOOST_CHECK_MESSAGE(false, "Missing auto script_invalid test: " + test.GetComment()); +#endif + strBad += write_string(Value(test.GetJSON()), true) + ",\n"; + } else { + BOOST_CHECK_MESSAGE(ParseScript(tests_bad[test.GetComment()][1].get_str()) == test.GetScriptPubKey(), "ScriptPubKey mismatch in auto script_invalid test: " + test.GetComment()); + strBad += write_string(Value(tests_bad[test.GetComment()]), true) + ",\n"; + } } -#if 0 +#ifdef UPDATE_JSON_TESTS FILE* valid = fopen("script_valid.json.gen", "w"); fputs(strGood.c_str(), valid); fclose(valid); @@ -464,18 +527,18 @@ BOOST_AUTO_TEST_CASE(script_PushData) static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a }; vector<vector<unsigned char> > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, true)); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), true, BaseSignatureChecker())); vector<vector<unsigned char> > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, true)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), true, BaseSignatureChecker())); BOOST_CHECK(pushdata1Stack == directStack); vector<vector<unsigned char> > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, true)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), true, BaseSignatureChecker())); BOOST_CHECK(pushdata2Stack == directStack); vector<vector<unsigned char> > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, true)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), true, BaseSignatureChecker())); BOOST_CHECK(pushdata4Stack == directStack); } @@ -525,15 +588,15 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, flags)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, flags)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) @@ -553,46 +616,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector<CKey> keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, flags)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); } BOOST_AUTO_TEST_CASE(script_combineSigs) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 6e5f0e3fac..afd63d2717 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -41,7 +41,7 @@ struct TestingSetup { mapArgs["-datadir"] = pathTemp.string(); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); - pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(); #ifdef ENABLE_WALLET bool fFirstRun; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 83116b51e5..18cb8f3d1b 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -27,11 +27,11 @@ using namespace boost::algorithm; // In script_tests.cpp extern Array read_json(const std::string& jsondata); -// Note how NOCACHE is not included as it is a runtime-only flag. static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of (string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE) (string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH) (string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC) + (string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG) (string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S) (string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY); @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - tx, i, verify_flags), + verify_flags, SignatureChecker(tx, i)), strTest); } } @@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - tx, i, verify_flags); + verify_flags, SignatureChecker(tx, i)); } BOOST_CHECK_MESSAGE(!fValid, strTest); @@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) // paid to a TX_PUBKEYHASH. // static std::vector<CMutableTransaction> -SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) +SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) { std::vector<CMutableTransaction> dummyTransactions; dummyTransactions.resize(2); @@ -262,14 +262,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG; dummyTransactions[0].vout[1].nValue = 50*CENT; dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG; - coinsRet.SetCoins(dummyTransactions[0].GetHash(), CCoins(dummyTransactions[0], 0)); + 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.SetCoins(dummyTransactions[1].GetHash(), CCoins(dummyTransactions[1], 0)); + coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0); return dummyTransactions; } @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(test_Get) { CBasicKeyStore keystore; CCoinsView coinsDummy; - CCoinsViewCache coins(coinsDummy); + CCoinsViewCache coins(&coinsDummy); std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); CMutableTransaction t1; @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) LOCK(cs_main); CBasicKeyStore keystore; CCoinsView coinsDummy; - CCoinsViewCache coins(coinsDummy); + CCoinsViewCache coins(&coinsDummy); std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); CMutableTransaction t; diff --git a/src/txdb.cpp b/src/txdb.cpp index d5f424fabd..cb9f150011 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -33,12 +33,6 @@ bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { return db.Read(make_pair('c', txid), coins); } -bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { - CLevelDBBatch batch; - BatchWriteCoins(batch, txid, coins); - return db.WriteBatch(batch); -} - bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { return db.Exists(make_pair('c', txid)); } @@ -50,24 +44,23 @@ uint256 CCoinsViewDB::GetBestBlock() const { return hashBestChain; } -bool CCoinsViewDB::SetBestBlock(const uint256 &hashBlock) { - CLevelDBBatch batch; - BatchWriteHashBestChain(batch, hashBlock); - return db.WriteBatch(batch); -} - bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { - LogPrint("coindb", "Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); - CLevelDBBatch batch; + size_t count = 0; + size_t changed = 0; for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { - BatchWriteCoins(batch, it->first, it->second); + if (it->second.flags & CCoinsCacheEntry::DIRTY) { + BatchWriteCoins(batch, it->first, it->second.coins); + changed++; + } + count++; CCoinsMap::iterator itOld = it++; mapCoins.erase(itOld); } if (hashBlock != uint256(0)) BatchWriteHashBestChain(batch, hashBlock); + LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return db.WriteBatch(batch); } diff --git a/src/txdb.h b/src/txdb.h index f0b6b9e1dd..8f2bd9af4d 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -33,10 +33,8 @@ public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); bool GetCoins(const uint256 &txid, CCoins &coins) const; - bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid) const; uint256 GetBestBlock() const; - bool SetBestBlock(const uint256 &hashBlock); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats) const; }; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d923c2204a..fa1802ad31 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -631,7 +631,7 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash) } -CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const { // If an entry in the mempool exists, always return that one, as it's guaranteed to never diff --git a/src/txmempool.h b/src/txmempool.h index c35ea52d4e..c63fd6f590 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -144,7 +144,7 @@ protected: CTxMemPool &mempool; public: - CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn); bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; }; diff --git a/src/wallet.cpp b/src/wallet.cpp index 1653084495..19e43f6ec2 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -15,6 +15,8 @@ #include "util.h" #include "utilmoneystr.h" +#include <assert.h> + #include <boost/algorithm/string/replace.hpp> #include <boost/thread.hpp> @@ -447,17 +449,25 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; if (fFileBacked) { + assert(!pwalletdbEncryption); pwalletdbEncryption = new CWalletDB(strWalletFile); - if (!pwalletdbEncryption->TxnBegin()) + if (!pwalletdbEncryption->TxnBegin()) { + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; return false; + } pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } if (!EncryptKeys(vMasterKey)) { - if (fFileBacked) + if (fFileBacked) { pwalletdbEncryption->TxnAbort(); - exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet. + delete pwalletdbEncryption; + } + // We now probably have half of our keys encrypted in memory, and half not... + // die and let the user reload their unencrypted wallet. + assert(false); } // Encryption was introduced in version 0.4.0 @@ -465,8 +475,12 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) if (fFileBacked) { - if (!pwalletdbEncryption->TxnCommit()) - exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. + if (!pwalletdbEncryption->TxnCommit()) { + delete pwalletdbEncryption; + // We now have keys encrypted in memory, but no on disk... + // die to avoid confusion and let the user reload their unencrypted wallet. + assert(false); + } delete pwalletdbEncryption; pwalletdbEncryption = NULL; @@ -1089,7 +1103,7 @@ CAmount CWallet::GetWatchOnlyBalance() const nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } - + return nTotal; } diff --git a/src/wallet.h b/src/wallet.h index 1ccc29d3c8..fa8a94dfc1 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -143,6 +143,7 @@ public: { SetNull(); } + CWallet(std::string strWalletFileIn) { SetNull(); @@ -150,6 +151,13 @@ public: strWalletFile = strWalletFileIn; fFileBacked = true; } + + ~CWallet() + { + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + void SetNull() { nWalletVersion = FEATURE_BASE; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index a851db65cd..783f766f6f 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -15,11 +15,11 @@ #include <boost/filesystem.hpp> #include <boost/foreach.hpp> +#include <boost/scoped_ptr.hpp> #include <boost/thread.hpp> -using namespace std; using namespace boost; - +using namespace std; static uint64_t nAccountingEntryNumber = 0; @@ -924,7 +924,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); bool fSuccess = allOK; - Db* pdbCopy = new Db(&dbenv.dbenv, 0); + boost::scoped_ptr<Db> pdbCopy(new Db(&dbenv.dbenv, 0)); int ret = pdbCopy->open(NULL, // Txn pointer filename.c_str(), // Filename "main", // Logical db name @@ -965,7 +965,6 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) } ptxn->commit(0); pdbCopy->close(0); - delete pdbCopy; return fSuccess; } |