diff options
Diffstat (limited to 'src')
74 files changed, 1742 insertions, 1598 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 35fca6570f..9b7e99861d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -98,7 +98,12 @@ BITCOIN_CORE_H = \ rpcclient.h \ rpcprotocol.h \ rpcserver.h \ - script.h \ + script/interpreter.h \ + script/compressor.h \ + script/script.h \ + script/sign.h \ + script/standard.h \ + scriptutils.h \ serialize.h \ sync.h \ threadsafety.h \ @@ -206,7 +211,12 @@ libbitcoin_common_a_SOURCES = \ keystore.cpp \ netbase.cpp \ protocol.cpp \ - script.cpp \ + script/interpreter.cpp \ + script/compressor.cpp \ + script/script.cpp \ + script/sign.cpp \ + script/standard.cpp \ + scriptutils.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. diff --git a/src/addrman.cpp b/src/addrman.cpp index 704766dbf8..68948ac7ff 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -45,13 +45,13 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const if (nTime > nNow + 10*60) // came in a flying DeLorean return true; - if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*86400) // not seen in over a month + if (nTime==0 || nNow-nTime > ADDRMAN_HORIZON_DAYS*24*60*60) // not seen in recent history return true; - if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried three times and never a success + if (nLastSuccess==0 && nAttempts>=ADDRMAN_RETRIES) // tried N times and never a success return true; - if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*86400 && nAttempts>=ADDRMAN_MAX_FAILURES) // 10 successive failures in the last week + if (nNow-nLastSuccess > ADDRMAN_MIN_FAIL_DAYS*24*60*60 && nAttempts>=ADDRMAN_MAX_FAILURES) // N successive failures in the last week return true; return false; diff --git a/src/addrman.h b/src/addrman.h index 0790802b5c..5fd698f18a 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -46,7 +46,7 @@ private: public: - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -268,7 +268,7 @@ public: // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports // changes to the ADDRMAN_ parameters without breaking the on-disk structure. // - // We don't use IMPLEMENT_SERIALIZE since the serialization and deserialization code has + // We don't use ADD_SERIALIZE_METHODS since the serialization and deserialization code has // very little in common. template<typename Stream> void Serialize(Stream &s, int nType, int nVersionDummy) const @@ -424,7 +424,7 @@ public: Check(); } if (fRet) - LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString(), nTried, nNew); + LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew); return fRet; } diff --git a/src/alert.h b/src/alert.h index 4a8736d60b..5ecf94cea8 100644 --- a/src/alert.h +++ b/src/alert.h @@ -46,7 +46,7 @@ public: std::string strStatusBar; std::string strReserved; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -84,7 +84,7 @@ public: SetNull(); } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { diff --git a/src/base58.h b/src/base58.h index 70681f589a..216aca3648 100644 --- a/src/base58.h +++ b/src/base58.h @@ -16,7 +16,7 @@ #include "chainparams.h" #include "key.h" -#include "script.h" +#include "script/script.h" #include <string> #include <vector> diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index c8cd9edfa4..91525b51c9 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -8,6 +8,8 @@ #include "core.h" #include "main.h" // for MAX_BLOCK_SIZE #include "keystore.h" +#include "script/script.h" +#include "script/sign.h" #include "ui_interface.h" // for _(...) #include "univalue/univalue.h" #include "core_io.h" diff --git a/src/bloom.cpp b/src/bloom.cpp index e34041336f..cef74a3a54 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -5,7 +5,8 @@ #include "bloom.h" #include "core.h" -#include "script.h" +#include "script/script.h" +#include "script/standard.h" #include <math.h> #include <stdlib.h> diff --git a/src/bloom.h b/src/bloom.h index 4a710928cc..143e3b4c79 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -62,7 +62,7 @@ public: CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); CBloomFilter() : isFull(true), isEmpty(false), nHashFuncs(0), nTweak(0), nFlags(0) {} - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ce99f268f3..460fabc6e6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -99,7 +99,6 @@ public: vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); vSeeds.push_back(CDNSSeedData("bitnodes.io", "seed.bitnodes.io")); - vSeeds.push_back(CDNSSeedData("open-nodes.org", "seeds.bitcoin.open-nodes.org")); vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); base58Prefixes[PUBKEY_ADDRESS] = list_of(0); @@ -156,6 +155,7 @@ public: vSeeds.push_back(CDNSSeedData("alexykot.me", "testnet-seed.alexykot.me")); vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org")); vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); + vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); base58Prefixes[PUBKEY_ADDRESS] = list_of(111); base58Prefixes[SCRIPT_ADDRESS] = list_of(196); diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 717f0b90fe..c41deea7ce 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -146,7 +146,7 @@ namespace Checkpoints { return checkpoints.rbegin()->first; } - CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex) + CBlockIndex* GetLastCheckpoint() { if (!fEnabled) return NULL; @@ -156,7 +156,7 @@ namespace Checkpoints { BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) { const uint256& hash = i.second; - std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash); + BlockMap::const_iterator t = mapBlockIndex.find(hash); if (t != mapBlockIndex.end()) return t->second; } diff --git a/src/checkpoints.h b/src/checkpoints.h index 52cdc35559..6d3f2d4935 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -22,7 +22,7 @@ namespace Checkpoints { int GetTotalBlocksEstimate(); // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint - CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex); + CBlockIndex* GetLastCheckpoint(); double GuessVerificationProgress(CBlockIndex *pindex, bool fSigchecks = true); diff --git a/src/core.h b/src/core.h index 34c00c4142..030eb17734 100644 --- a/src/core.h +++ b/src/core.h @@ -6,7 +6,8 @@ #ifndef BITCOIN_CORE_H #define BITCOIN_CORE_H -#include "script.h" +#include "script/compressor.h" +#include "script/script.h" #include "serialize.h" #include "uint256.h" @@ -31,7 +32,7 @@ public: COutPoint() { SetNull(); } COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -91,7 +92,7 @@ public: explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max()); CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits<uint32_t>::max()); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -145,7 +146,7 @@ public: friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } std::string ToString() const; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -170,7 +171,7 @@ public: CTxOut(int64_t nValueIn, CScript scriptPubKeyIn); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -253,18 +254,16 @@ public: CTransaction& operator=(const CTransaction& tx); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - READWRITE(*const_cast<int32_t*>(&this->nVersion)); nVersion = this->nVersion; READWRITE(*const_cast<std::vector<CTxIn>*>(&vin)); READWRITE(*const_cast<std::vector<CTxOut>*>(&vout)); READWRITE(*const_cast<uint32_t*>(&nLockTime)); - if (fRead) + if (ser_action.ForRead()) UpdateHash(); } @@ -313,7 +312,7 @@ struct CMutableTransaction CMutableTransaction(); CMutableTransaction(const CTransaction& tx); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -342,12 +341,11 @@ public: CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - if (!fRead) { + if (!ser_action.ForRead()) { uint64_t nVal = CompressAmount(txout.nValue); READWRITE(VARINT(nVal)); } else { @@ -410,7 +408,7 @@ public: // undo information for all txins std::vector<CTxInUndo> vprevout; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -443,7 +441,7 @@ public: SetNull(); } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -500,7 +498,7 @@ public: *((CBlockHeader*)this) = header; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -550,7 +548,7 @@ struct CBlockLocator vHave = vHaveIn; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { diff --git a/src/core_read.cpp b/src/core_read.cpp index 57f1397f18..efcecb106f 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -5,7 +5,7 @@ #include "core_io.h" #include "core.h" #include "serialize.h" -#include "script.h" +#include "script/script.h" #include "util.h" #include <boost/assign/list_of.hpp> diff --git a/src/core_write.cpp b/src/core_write.cpp index e66e75515c..62712b1ba0 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -4,7 +4,8 @@ #include "core_io.h" #include "univalue/univalue.h" -#include "script.h" +#include "script/script.h" +#include "script/standard.h" #include "core.h" #include "serialize.h" #include "util.h" diff --git a/src/crypter.cpp b/src/crypter.cpp index 8aa2bb0517..3df13021df 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -4,7 +4,7 @@ #include "crypter.h" -#include "script.h" +#include "script/script.h" #include "util.h" #include <string> diff --git a/src/crypter.h b/src/crypter.h index 59efc76508..c7424c9b20 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -43,7 +43,7 @@ public: // such as the various parameters to scrypt std::vector<unsigned char> vchOtherDerivationParameters; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { diff --git a/src/init.cpp b/src/init.cpp index b8988f8b75..31f64878fb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1031,7 +1031,7 @@ bool AppInit2(boost::thread_group& threadGroup) { string strMatch = mapArgs["-printblock"]; int nFound = 0; - for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { uint256 hash = (*mi).first; if (boost::algorithm::starts_with(hash.ToString(), strMatch)) diff --git a/src/keystore.cpp b/src/keystore.cpp index 72ae9b0a30..98bc0e9e28 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -7,7 +7,7 @@ #include "crypter.h" #include "key.h" -#include "script.h" +#include "script/script.h" #include "util.h" #include <boost/foreach.hpp> diff --git a/src/main.cpp b/src/main.cpp index bea01ab7c9..bbe5bd87fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,7 +38,7 @@ using namespace boost; CCriticalSection cs_main; -map<uint256, CBlockIndex*> mapBlockIndex; +BlockMap mapBlockIndex; CChain chainActive; int64_t nTimeBestReceived = 0; CWaitableCriticalSection csBestBlock; @@ -328,7 +328,7 @@ void ProcessBlockAvailability(NodeId nodeid) { assert(state != NULL); if (state->hashLastUnknownBlock != 0) { - map<uint256, CBlockIndex*>::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); + BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = itOld->second; @@ -344,7 +344,7 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { ProcessBlockAvailability(nodeid); - map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hash); + BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { // An actually better block was announced. if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) @@ -434,7 +434,7 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { // Find the first block the caller has in the main chain BOOST_FOREACH(const uint256& hash, locator.vHave) { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); + BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; @@ -492,16 +492,17 @@ bool AddOrphanTx(const CTransaction& tx) void static EraseOrphanTx(uint256 hash) { - if (!mapOrphanTransactions.count(hash)) + map<uint256, CTransaction>::iterator it = mapOrphanTransactions.find(hash); + if (it == mapOrphanTransactions.end()) return; - const CTransaction& tx = mapOrphanTransactions[hash]; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + BOOST_FOREACH(const CTxIn& txin, it->second.vin) { - mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); - if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) - mapOrphanTransactionsByPrev.erase(txin.prevout.hash); + map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); + itPrev->second.erase(hash); + if (itPrev->second.empty()) + mapOrphanTransactionsByPrev.erase(itPrev); } - mapOrphanTransactions.erase(hash); + mapOrphanTransactions.erase(it); } unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) @@ -2070,7 +2071,7 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) { // Check for duplicate uint256 hash = block.GetHash(); - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hash); + BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end()) return it->second; @@ -2081,9 +2082,9 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) LOCK(cs_nBlockSequenceId); pindexNew->nSequenceId = nBlockSequenceId++; } - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); - map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); + BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; @@ -2296,7 +2297,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex AssertLockHeld(cs_main); // Check for duplicate uint256 hash = block.GetHash(); - std::map<uint256, CBlockIndex*>::iterator miSelf = mapBlockIndex.find(hash); + BlockMap::iterator miSelf = mapBlockIndex.find(hash); CBlockIndex *pindex = NULL; if (miSelf != mapBlockIndex.end()) { pindex = miSelf->second; @@ -2304,7 +2305,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex return state.Invalid(error("AcceptBlock() : block is marked invalid"), 0, "duplicate"); } - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) { // Extra checks to prevent "fill up memory by spamming with bogus blocks" @@ -2325,7 +2326,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex CBlockIndex* pindexPrev = NULL; int nHeight = 0; if (hash != Params().HashGenesisBlock()) { - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("AcceptBlock() : prev block not found"), 0, "bad-prevblk"); pindexPrev = (*mi).second; @@ -2347,7 +2348,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex REJECT_CHECKPOINT, "checkpoint mismatch"); // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); if (pcheckpoint && nHeight < pcheckpoint->nHeight) return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); @@ -2519,7 +2520,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return error("ProcessBlock() : CheckBlock FAILED"); // If we don't already have its previous block (with full data), shunt it off to holding area until we get it - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pblock->hashPrevBlock); + BlockMap::iterator it = mapBlockIndex.find(pblock->hashPrevBlock); if (pblock->hashPrevBlock != 0 && (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA))) { LogPrintf("ProcessBlock: ORPHAN BLOCK %lu, prev=%s\n", (unsigned long)mapOrphanBlocks.size(), pblock->hashPrevBlock.ToString()); @@ -2801,7 +2802,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) return NULL; // Return existing - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); + BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) return (*mi).second; @@ -2878,7 +2879,7 @@ bool static LoadBlockIndexDB() LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); // Load pointer to end of best chain - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) return true; chainActive.SetTip(it->second); @@ -3036,7 +3037,7 @@ void PrintBlockTree() AssertLockHeld(cs_main); // pre-compute tree structure map<CBlockIndex*, vector<CBlockIndex*> > mapNext; - for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { CBlockIndex* pindex = (*mi).second; mapNext[pindex->pprev].push_back(pindex); @@ -3282,13 +3283,13 @@ void static ProcessGetData(CNode* pfrom) if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { bool send = false; - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); + BlockMap::iterator mi = mapBlockIndex.find(inv.hash); if (mi != mapBlockIndex.end()) { // If the requested block is at a height below our last // checkpoint, only serve it if it's in the checkpointed chain int nHeight = mi->second->nHeight; - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); if (pcheckpoint && nHeight < pcheckpoint->nHeight) { if (!chainActive.Contains(mi->second)) { @@ -3713,7 +3714,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (locator.IsNull()) { // If locator is null, return the hashStop block - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashStop); + BlockMap::iterator mi = mapBlockIndex.find(hashStop); if (mi == mapBlockIndex.end()) return true; pindex = (*mi).second; @@ -3771,9 +3772,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { - uint256 hashPrev = vWorkQueue[i]; - for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); - mi != mapOrphanTransactionsByPrev[hashPrev].end(); + map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); + if (itByPrev == mapOrphanTransactionsByPrev.end()) + continue; + for (set<uint256>::iterator mi = itByPrev->second.begin(); + mi != itByPrev->second.end(); ++mi) { const uint256& orphanHash = *mi; @@ -4138,7 +4141,7 @@ bool ProcessMessages(CNode* pfrom) // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { - LogPrintf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); + LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", msg.hdr.GetCommand(), pfrom->id); fOk = false; break; } @@ -4147,7 +4150,7 @@ bool ProcessMessages(CNode* pfrom) CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { - LogPrintf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand()); + LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", hdr.GetCommand(), pfrom->id); continue; } string strCommand = hdr.GetCommand(); @@ -4394,7 +4397,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!pto->fDisconnect && state.nBlocksInFlight && state.nLastBlockReceive < state.nLastBlockProcess - BLOCK_DOWNLOAD_TIMEOUT*1000000 && state.vBlocksInFlight.front().nTime < state.nLastBlockProcess - 2*BLOCK_DOWNLOAD_TIMEOUT*1000000) { - LogPrintf("Peer %s is stalling block download, disconnecting\n", state.name.c_str()); + LogPrintf("Peer %s is stalling block download, disconnecting\n", state.name); pto->fDisconnect = true; } @@ -4504,7 +4507,7 @@ bool CBlockUndo::ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock } std::string CBlockFileInfo::ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); } @@ -4515,7 +4518,7 @@ public: CMainCleanup() {} ~CMainCleanup() { // block headers - std::map<uint256, CBlockIndex*>::iterator it1 = mapBlockIndex.begin(); + BlockMap::iterator it1 = mapBlockIndex.begin(); for (; it1 != mapBlockIndex.end(); it1++) delete (*it1).second; mapBlockIndex.clear(); diff --git a/src/main.h b/src/main.h index 31a1131b83..30cccab2f1 100644 --- a/src/main.h +++ b/src/main.h @@ -15,7 +15,8 @@ #include "core.h" #include "net.h" #include "pow.h" -#include "script.h" +#include "script/script.h" +#include "script/standard.h" #include "sync.h" #include "txmempool.h" #include "uint256.h" @@ -29,6 +30,8 @@ #include <utility> #include <vector> +#include <boost/unordered_map.hpp> + class CBlockIndex; class CBloomFilter; class CInv; @@ -81,11 +84,16 @@ static const unsigned char REJECT_DUST = 0x41; static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; static const unsigned char REJECT_CHECKPOINT = 0x43; +struct BlockHasher +{ + size_t operator()(const uint256& hash) const { return hash.GetLow64(); } +}; extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern CTxMemPool mempool; -extern std::map<uint256, CBlockIndex*> mapBlockIndex; +typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap; +extern BlockMap mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; @@ -197,7 +205,7 @@ struct CDiskBlockPos int nFile; unsigned int nPos; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -230,7 +238,7 @@ struct CDiskTxPos : public CDiskBlockPos { unsigned int nTxOffset; // after header - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -313,7 +321,7 @@ class CBlockUndo public: std::vector<CTxUndo> vtxundo; // for all but the coinbase - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -420,16 +428,14 @@ protected: public: // serialization implementation - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - READWRITE(nTransactions); READWRITE(vHash); std::vector<unsigned char> vBytes; - if (fRead) { + if (ser_action.ForRead()) { READWRITE(vBytes); CPartialMerkleTree &us = *(const_cast<CPartialMerkleTree*>(this)); us.vBits.resize(vBytes.size() * 8); @@ -498,7 +504,7 @@ public: uint64_t nTimeFirst; // earliest time of block in file uint64_t nTimeLast; // latest time of block in file - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -548,15 +554,16 @@ enum BlockStatus { BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok - BLOCK_VALID_MASK = 7, + BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS | + BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS, BLOCK_HAVE_DATA = 8, // full block available in blk*.dat BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat - BLOCK_HAVE_MASK = 24, + BLOCK_HAVE_MASK = BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO, BLOCK_FAILED_VALID = 32, // stage after last reached validness failed BLOCK_FAILED_CHILD = 64, // descends from failed block - BLOCK_FAILED_MASK = 96 + BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, }; /** The block chain is a tree shaped structure starting with the @@ -772,7 +779,7 @@ public: hashPrev = (pprev ? pprev->GetBlockHash() : 0); } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -994,7 +1001,7 @@ public: // thus the filter will likely be modified. CMerkleBlock(const CBlock& block, CBloomFilter& filter); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { diff --git a/src/net.cpp b/src/net.cpp index 3966706207..633a3a34e7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1080,8 +1080,6 @@ void ThreadSocketHandler() BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } - - MilliSleep(10); } } @@ -1791,7 +1789,6 @@ bool StopNode() if (semOutbound) for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) semOutbound->post(); - MilliSleep(50); DumpAddresses(); return true; @@ -2117,7 +2114,7 @@ void CNode::AskFor(const CInv& inv) nRequestTime = it->second; else nRequestTime = 0; - LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str(), id); + LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id); // Make sure not to reuse time indexes to keep things in the same order int64_t nNow = GetTimeMicros() - 1000000; diff --git a/src/netbase.cpp b/src/netbase.cpp index d5821d4465..954c11f77d 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -259,7 +259,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) strSocks5 += strDest; strSocks5 += static_cast<char>((port >> 8) & 0xFF); strSocks5 += static_cast<char>((port >> 0) & 0xFF); - ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL); + ret = send(hSocket, strSocks5.data(), strSocks5.size(), MSG_NOSIGNAL); if (ret != (ssize_t)strSocks5.size()) { CloseSocket(hSocket); diff --git a/src/netbase.h b/src/netbase.h index 9b52c0a415..9fc5c72eb8 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -88,7 +88,7 @@ class CNetAddr friend bool operator!=(const CNetAddr& a, const CNetAddr& b); friend bool operator<(const CNetAddr& a, const CNetAddr& b); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -150,15 +150,14 @@ class CService : public CNetAddr CService(const struct in6_addr& ipv6Addr, unsigned short port); CService(const struct sockaddr_in6& addr); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); READWRITE(FLATDATA(ip)); unsigned short portN = htons(port); READWRITE(portN); - if (fRead) + if (ser_action.ForRead()) port = ntohs(portN); } }; diff --git a/src/protocol.h b/src/protocol.h index e4b0991774..82d29e66de 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -35,7 +35,7 @@ class CMessageHeader std::string GetCommand() const; bool IsValid() const; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -85,20 +85,17 @@ class CAddress : public CService void Init(); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - - CAddress* pthis = const_cast<CAddress*>(this); - if (fRead) - pthis->Init(); + if (ser_action.ForRead()) + Init(); if (nType & SER_DISK) READWRITE(nVersion); if ((nType & SER_DISK) || (nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH))) - READWRITE(nTime); + READWRITE(nTime); READWRITE(nServices); READWRITE(*(CService*)this); } @@ -122,7 +119,7 @@ class CInv CInv(int typeIn, const uint256& hashIn); CInv(const std::string& strType, const uint256& hashIn); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 7b30f8de09..d10463fd8f 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -705,7 +705,7 @@ void CoinControlDialog::updateView() QString sAddress = ""; if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress)) { - sAddress = CBitcoinAddress(outputAddress).ToString().c_str(); + sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString()); // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs if (!treeMode || (!(sAddress == sWalletAddress))) @@ -752,7 +752,7 @@ void CoinControlDialog::updateView() // transaction hash uint256 txhash = out.tx->GetHash(); - itemOutput->setText(COLUMN_TXHASH, txhash.GetHex().c_str()); + itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); // vout index itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 15501b8a8a..90762bea5d 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -172,7 +172,7 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 // for symmetry reasons also show immature label when the watch-only one is shown ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); - ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance + ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance } // show/hide watch-only labels diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index a558aa4942..3df5971823 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -24,23 +24,19 @@ public: QDateTime date; SendCoinsRecipient recipient; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - - RecentRequestEntry* pthis = const_cast<RecentRequestEntry*>(this); - unsigned int nDate = date.toTime_t(); - READWRITE(pthis->nVersion); - nVersion = pthis->nVersion; + READWRITE(this->nVersion); + nVersion = this->nVersion; READWRITE(id); READWRITE(nDate); READWRITE(recipient); - if (fRead) + if (ser_action.ForRead()) date = QDateTime::fromTime_t(nDate); } }; diff --git a/src/qt/test/paymentrequestdata.h b/src/qt/test/paymentrequestdata.h index 49558165f0..aeaa7d89a0 100644 --- a/src/qt/test/paymentrequestdata.h +++ b/src/qt/test/paymentrequestdata.h @@ -1,3 +1,7 @@ +// 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. + // // Data for paymentservertests.cpp // diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index e92a7d2b1a..5d7fe96285 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -1,3 +1,7 @@ +// 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 "paymentservertests.h" #include "optionsmodel.h" @@ -21,7 +25,6 @@ X509 *parse_b64der_cert(const char* cert_data) return cert; } - // // Test payment request handling // @@ -30,7 +33,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig { RecipientCatcher sigCatcher; QObject::connect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), - &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); + &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); // Write data to a temp file: QTemporaryFile f; @@ -48,7 +51,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig QCoreApplication::sendEvent(&object, &event); QObject::disconnect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), - &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); + &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); // Return results from sigCatcher return sigCatcher.recipient; diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h index 884e535a60..9b6400b0d5 100644 --- a/src/qt/test/paymentservertests.h +++ b/src/qt/test/paymentservertests.h @@ -1,3 +1,7 @@ +// 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 PAYMENTSERVERTESTS_H #define PAYMENTSERVERTESTS_H @@ -15,8 +19,8 @@ private slots: }; // Dummy class to receive paymentserver signals. -// If SendCoinsRecipient was a proper QObject, then we could use -// QSignalSpy... but it's not. +// If SendCoinsRecipient was a proper QObject, then +// we could use QSignalSpy... but it's not. class RecipientCatcher : public QObject { Q_OBJECT diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 03a2381c06..f2161c2f79 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,11 +1,16 @@ +// 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. + #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif +#include "uritests.h" + #ifdef ENABLE_WALLET #include "paymentservertests.h" #endif -#include "uritests.h" #include <QCoreApplication> #include <QObject> diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index 5c0f4406a7..78a7b1b9b4 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -1,3 +1,7 @@ +// 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 "uritests.h" #include "guiutil.h" diff --git a/src/qt/test/uritests.h b/src/qt/test/uritests.h index 17d4280a90..1ea6d9f075 100644 --- a/src/qt/test/uritests.h +++ b/src/qt/test/uritests.h @@ -1,3 +1,7 @@ +// 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 URITESTS_H #define URITESTS_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 8258e719a3..727b8dc66d 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -11,7 +11,7 @@ #include "db.h" #include "main.h" #include "paymentserver.h" -#include "script.h" +#include "script/script.h" #include "transactionrecord.h" #include "timedata.h" #include "ui_interface.h" diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index d7bd25e08b..20c1449c92 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -170,7 +170,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // Find the block the tx is in CBlockIndex* pindex = NULL; - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock); + BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock); if (mi != mapBlockIndex.end()) pindex = (*mi).second; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 1a1f726bf8..734c7afc4e 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -394,19 +394,25 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const { + QString watchAddress; + if (tooltip) { + // Mark transactions involving watch-only addresses by adding " (watch-only)" + watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : ""; + } + switch(wtx->type) { case TransactionRecord::RecvFromOther: - return QString::fromStdString(wtx->address); + return QString::fromStdString(wtx->address) + watchAddress; case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: case TransactionRecord::Generated: - return lookupAddress(wtx->address, tooltip); + return lookupAddress(wtx->address, tooltip) + watchAddress; case TransactionRecord::SendToOther: - return QString::fromStdString(wtx->address); + return QString::fromStdString(wtx->address) + watchAddress; case TransactionRecord::SendToSelf: default: - return tr("(n/a)"); + return tr("(n/a)") + watchAddress; } } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 2d34d58129..a7ba100cd2 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -331,7 +331,7 @@ void TransactionView::exportClicked() writer.setModel(transactionProxyModel); writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); if (model && model->haveWatchOnly()) - writer.addColumn(tr("Watchonly"), TransactionTableModel::Watchonly); + writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly); writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 92c22f5692..8d2c2e96d8 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -36,6 +36,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p { fProcessingQueuedTransactions = false; fHaveWatchOnly = wallet->HaveWatchOnly(); + fForceCheckBalanceChanged = false; addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); @@ -121,8 +122,10 @@ void WalletModel::pollBalanceChanged() if(!lockWallet) return; - if(chainActive.Height() != cachedNumBlocks) + if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks) { + fForceCheckBalanceChanged = false; + // Balance and number of transactions might have changed cachedNumBlocks = chainActive.Height(); @@ -167,7 +170,7 @@ void WalletModel::updateTransaction(const QString &hash, int status) transactionTableModel->updateTransaction(hash, status); // Balance and number of transactions might have changed - checkBalanceChanged(); + fForceCheckBalanceChanged = true; } void WalletModel::updateAddressBook(const QString &address, const QString &label, @@ -344,6 +347,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran } emit coinsSent(wallet, rcp, transaction_array); } + checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); } @@ -473,11 +477,6 @@ static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress) { - // emits signal "showProgress" - QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(title)), - Q_ARG(int, nProgress)); - if (nProgress == 0) fQueueNotifications = true; @@ -495,6 +494,11 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int } std::vector<std::pair<uint256, ChangeType> >().swap(vQueueNotifications); // clear } + + // emits signal "showProgress" + QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(title)), + Q_ARG(int, nProgress)); } static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly) @@ -618,7 +622,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) CTxDestination address; if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue; - mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out); + mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out); } } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 2a9ac4650f..111ae2178c 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -59,24 +59,20 @@ public: static const int CURRENT_VERSION = 1; int nVersion; - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - - SendCoinsRecipient* pthis = const_cast<SendCoinsRecipient*>(this); - - std::string sAddress = pthis->address.toStdString(); - std::string sLabel = pthis->label.toStdString(); - std::string sMessage = pthis->message.toStdString(); + std::string sAddress = address.toStdString(); + std::string sLabel = label.toStdString(); + std::string sMessage = message.toStdString(); std::string sPaymentRequest; - if (!fRead && pthis->paymentRequest.IsInitialized()) - pthis->paymentRequest.SerializeToString(&sPaymentRequest); - std::string sAuthenticatedMerchant = pthis->authenticatedMerchant.toStdString(); + if (!ser_action.ForRead() && paymentRequest.IsInitialized()) + paymentRequest.SerializeToString(&sPaymentRequest); + std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString(); - READWRITE(pthis->nVersion); - nVersion = pthis->nVersion; + READWRITE(this->nVersion); + nVersion = this->nVersion; READWRITE(sAddress); READWRITE(sLabel); READWRITE(amount); @@ -84,14 +80,14 @@ public: READWRITE(sPaymentRequest); READWRITE(sAuthenticatedMerchant); - if (fRead) + if (ser_action.ForRead()) { - pthis->address = QString::fromStdString(sAddress); - pthis->label = QString::fromStdString(sLabel); - pthis->message = QString::fromStdString(sMessage); + address = QString::fromStdString(sAddress); + label = QString::fromStdString(sLabel); + message = QString::fromStdString(sMessage); if (!sPaymentRequest.empty()) - pthis->paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size())); - pthis->authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); + paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size())); + authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); } } }; @@ -203,6 +199,7 @@ private: CWallet *wallet; bool fProcessingQueuedTransactions; bool fHaveWatchOnly; + bool fForceCheckBalanceChanged; // Wallet has an options model for wallet-specific options // (transaction fee, for example) @@ -260,7 +257,7 @@ public slots: void updateTransaction(const QString &hash, int status); /* New, updated or removed address book entry */ void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); - /* Watchonly added */ + /* Watch-only added */ void updateWatchOnlyFlag(bool fHaveWatchonly); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ void pollBalanceChanged(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 83fe629351..4b3beae20c 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -392,7 +392,7 @@ Value gettxout(const Array& params, bool fHelp) if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) return Value::null; - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); CBlockIndex *pindex = it->second; ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index bd992397b8..917c840536 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -74,8 +74,8 @@ Value getinfo(const Array& params, bool fHelp) GetProxy(NET_IPV4, proxy); Object obj; - obj.push_back(Pair("version", (int)CLIENT_VERSION)); - obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); + obj.push_back(Pair("version", CLIENT_VERSION)); + obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); #ifdef ENABLE_WALLET if (pwalletMain) { obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index dc75caeabc..c5c99870fc 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -11,6 +11,9 @@ #include "main.h" #include "net.h" #include "rpcserver.h" +#include "script/script.h" +#include "script/standard.h" +#include "script/sign.h" #include "uint256.h" #ifdef ENABLE_WALLET #include "wallet.h" @@ -88,7 +91,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if (hashBlock != 0) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 215da2ea12..100d6c2bd0 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1447,7 +1447,7 @@ Value listsinceblock(const Array& params, bool fHelp) uint256 blockId = 0; blockId.SetHex(params[0].get_str()); - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(blockId); + BlockMap::iterator it = mapBlockIndex.find(blockId); if (it != mapBlockIndex.end()) pindex = it->second; } diff --git a/src/script/compressor.cpp b/src/script/compressor.cpp new file mode 100644 index 0000000000..2f8df602bf --- /dev/null +++ b/src/script/compressor.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "compressor.h" + +bool CScriptCompressor::IsToKeyID(CKeyID &hash) const +{ + if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 + && script[2] == 20 && script[23] == OP_EQUALVERIFY + && script[24] == OP_CHECKSIG) { + memcpy(&hash, &script[3], 20); + return true; + } + return false; +} + +bool CScriptCompressor::IsToScriptID(CScriptID &hash) const +{ + if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 + && script[22] == OP_EQUAL) { + memcpy(&hash, &script[2], 20); + return true; + } + return false; +} + +bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const +{ + if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG + && (script[1] == 0x02 || script[1] == 0x03)) { + pubkey.Set(&script[1], &script[34]); + return true; + } + if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG + && script[1] == 0x04) { + pubkey.Set(&script[1], &script[66]); + return pubkey.IsFullyValid(); // if not fully valid, a case that would not be compressible + } + return false; +} + +bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const +{ + CKeyID keyID; + if (IsToKeyID(keyID)) { + out.resize(21); + out[0] = 0x00; + memcpy(&out[1], &keyID, 20); + return true; + } + CScriptID scriptID; + if (IsToScriptID(scriptID)) { + out.resize(21); + out[0] = 0x01; + memcpy(&out[1], &scriptID, 20); + return true; + } + CPubKey pubkey; + if (IsToPubKey(pubkey)) { + out.resize(33); + memcpy(&out[1], &pubkey[1], 32); + if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { + out[0] = pubkey[0]; + return true; + } else if (pubkey[0] == 0x04) { + out[0] = 0x04 | (pubkey[64] & 0x01); + return true; + } + } + return false; +} + +unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const +{ + if (nSize == 0 || nSize == 1) + return 20; + if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5) + return 32; + return 0; +} + +bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigned char> &in) +{ + switch(nSize) { + case 0x00: + script.resize(25); + script[0] = OP_DUP; + script[1] = OP_HASH160; + script[2] = 20; + memcpy(&script[3], &in[0], 20); + script[23] = OP_EQUALVERIFY; + script[24] = OP_CHECKSIG; + return true; + case 0x01: + script.resize(23); + script[0] = OP_HASH160; + script[1] = 20; + memcpy(&script[2], &in[0], 20); + script[22] = OP_EQUAL; + return true; + case 0x02: + case 0x03: + script.resize(35); + script[0] = 33; + script[1] = nSize; + memcpy(&script[2], &in[0], 32); + script[34] = OP_CHECKSIG; + return true; + case 0x04: + case 0x05: + unsigned char vch[33] = {}; + vch[0] = nSize - 2; + memcpy(&vch[1], &in[0], 32); + CPubKey pubkey(&vch[0], &vch[33]); + if (!pubkey.Decompress()) + return false; + assert(pubkey.size() == 65); + script.resize(67); + script[0] = 65; + memcpy(&script[1], pubkey.begin(), 65); + script[66] = OP_CHECKSIG; + return true; + } + return false; +} diff --git a/src/script/compressor.h b/src/script/compressor.h new file mode 100644 index 0000000000..f0a3754f02 --- /dev/null +++ b/src/script/compressor.h @@ -0,0 +1,84 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPT_COMPRESSOR +#define H_BITCOIN_SCRIPT_COMPRESSOR + +#include "script/script.h" + +/** Compact serializer for scripts. + * + * It detects common cases and encodes them much more efficiently. + * 3 special cases are defined: + * * Pay to pubkey hash (encoded as 21 bytes) + * * Pay to script hash (encoded as 21 bytes) + * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) + * + * Other scripts up to 121 bytes require 1 byte + script length. Above + * that, scripts up to 16505 bytes require 2 bytes + script length. + */ +class CScriptCompressor +{ +private: + // make this static for now (there are only 6 special scripts defined) + // this can potentially be extended together with a new nVersion for + // transactions, in which case this value becomes dependent on nVersion + // and nHeight of the enclosing transaction. + static const unsigned int nSpecialScripts = 6; + + CScript &script; +protected: + // These check for scripts for which a special case with a shorter encoding is defined. + // They are implemented separately from the CScript test, as these test for exact byte + // sequence correspondences, and are more strict. For example, IsToPubKey also verifies + // whether the public key is valid (as invalid ones cannot be represented in compressed + // form). + bool IsToKeyID(CKeyID &hash) const; + bool IsToScriptID(CScriptID &hash) const; + bool IsToPubKey(CPubKey &pubkey) const; + + bool Compress(std::vector<unsigned char> &out) const; + unsigned int GetSpecialSize(unsigned int nSize) const; + bool Decompress(unsigned int nSize, const std::vector<unsigned char> &out); +public: + CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + std::vector<unsigned char> compr; + if (Compress(compr)) + return compr.size(); + unsigned int nSize = script.size() + nSpecialScripts; + return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion); + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + std::vector<unsigned char> compr; + if (Compress(compr)) { + s << CFlatData(compr); + return; + } + unsigned int nSize = script.size() + nSpecialScripts; + s << VARINT(nSize); + s << CFlatData(script); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nSize = 0; + s >> VARINT(nSize); + if (nSize < nSpecialScripts) { + std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); + s >> REF(CFlatData(vch)); + Decompress(nSize, vch); + return; + } + nSize -= nSpecialScripts; + script.resize(nSize); + s >> REF(CFlatData(script)); + } +}; + +#endif diff --git a/src/script.cpp b/src/script/interpreter.cpp index 21883bd41a..4f4fdb6b7f 100644 --- a/src/script.cpp +++ b/src/script/interpreter.cpp @@ -3,29 +3,21 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script.h" +#include "interpreter.h" +#include "core.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha2.h" -#include "core.h" -#include "hash.h" -#include "key.h" -#include "keystore.h" #include "random.h" -#include "sync.h" +#include "script/script.h" #include "uint256.h" #include "util.h" -#include <boost/foreach.hpp> #include <boost/thread.hpp> #include <boost/tuple/tuple_comparison.hpp> -#include <boost/tuple/tuple.hpp> - -#include <boost/foreach.hpp> using namespace std; -using namespace boost; typedef vector<unsigned char> valtype; static const valtype vchFalse(0); @@ -36,8 +28,6 @@ static const CScriptNum bnOne(1); static const CScriptNum bnFalse(0); static const CScriptNum bnTrue(1); -bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); - bool CastToBool(const valtype& vch) { for (unsigned int i = 0; i < vch.size(); i++) @@ -53,8 +43,6 @@ bool CastToBool(const valtype& vch) return false; } - - // // Script is a stack machine (like Forth) that evaluates a predicate // returning a bool indicating valid or not. There are no loops. @@ -68,165 +56,6 @@ static inline void popstack(vector<valtype>& stack) stack.pop_back(); } - -const char* GetTxnOutputType(txnouttype t) -{ - switch (t) - { - case TX_NONSTANDARD: return "nonstandard"; - case TX_PUBKEY: return "pubkey"; - case TX_PUBKEYHASH: return "pubkeyhash"; - case TX_SCRIPTHASH: return "scripthash"; - case TX_MULTISIG: return "multisig"; - case TX_NULL_DATA: return "nulldata"; - } - return NULL; -} - - -const char* GetOpName(opcodetype opcode) -{ - switch (opcode) - { - // push value - case OP_0 : return "0"; - case OP_PUSHDATA1 : return "OP_PUSHDATA1"; - case OP_PUSHDATA2 : return "OP_PUSHDATA2"; - case OP_PUSHDATA4 : return "OP_PUSHDATA4"; - case OP_1NEGATE : return "-1"; - case OP_RESERVED : return "OP_RESERVED"; - case OP_1 : return "1"; - case OP_2 : return "2"; - case OP_3 : return "3"; - case OP_4 : return "4"; - case OP_5 : return "5"; - case OP_6 : return "6"; - case OP_7 : return "7"; - case OP_8 : return "8"; - case OP_9 : return "9"; - case OP_10 : return "10"; - case OP_11 : return "11"; - case OP_12 : return "12"; - case OP_13 : return "13"; - case OP_14 : return "14"; - case OP_15 : return "15"; - case OP_16 : return "16"; - - // control - case OP_NOP : return "OP_NOP"; - case OP_VER : return "OP_VER"; - case OP_IF : return "OP_IF"; - case OP_NOTIF : return "OP_NOTIF"; - case OP_VERIF : return "OP_VERIF"; - case OP_VERNOTIF : return "OP_VERNOTIF"; - case OP_ELSE : return "OP_ELSE"; - case OP_ENDIF : return "OP_ENDIF"; - case OP_VERIFY : return "OP_VERIFY"; - case OP_RETURN : return "OP_RETURN"; - - // stack ops - case OP_TOALTSTACK : return "OP_TOALTSTACK"; - case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; - case OP_2DROP : return "OP_2DROP"; - case OP_2DUP : return "OP_2DUP"; - case OP_3DUP : return "OP_3DUP"; - case OP_2OVER : return "OP_2OVER"; - case OP_2ROT : return "OP_2ROT"; - case OP_2SWAP : return "OP_2SWAP"; - case OP_IFDUP : return "OP_IFDUP"; - case OP_DEPTH : return "OP_DEPTH"; - case OP_DROP : return "OP_DROP"; - case OP_DUP : return "OP_DUP"; - case OP_NIP : return "OP_NIP"; - case OP_OVER : return "OP_OVER"; - case OP_PICK : return "OP_PICK"; - case OP_ROLL : return "OP_ROLL"; - case OP_ROT : return "OP_ROT"; - case OP_SWAP : return "OP_SWAP"; - case OP_TUCK : return "OP_TUCK"; - - // splice ops - case OP_CAT : return "OP_CAT"; - case OP_SUBSTR : return "OP_SUBSTR"; - case OP_LEFT : return "OP_LEFT"; - case OP_RIGHT : return "OP_RIGHT"; - case OP_SIZE : return "OP_SIZE"; - - // bit logic - case OP_INVERT : return "OP_INVERT"; - case OP_AND : return "OP_AND"; - case OP_OR : return "OP_OR"; - case OP_XOR : return "OP_XOR"; - case OP_EQUAL : return "OP_EQUAL"; - case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; - case OP_RESERVED1 : return "OP_RESERVED1"; - case OP_RESERVED2 : return "OP_RESERVED2"; - - // numeric - case OP_1ADD : return "OP_1ADD"; - case OP_1SUB : return "OP_1SUB"; - case OP_2MUL : return "OP_2MUL"; - case OP_2DIV : return "OP_2DIV"; - case OP_NEGATE : return "OP_NEGATE"; - case OP_ABS : return "OP_ABS"; - case OP_NOT : return "OP_NOT"; - case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; - case OP_ADD : return "OP_ADD"; - case OP_SUB : return "OP_SUB"; - case OP_MUL : return "OP_MUL"; - case OP_DIV : return "OP_DIV"; - case OP_MOD : return "OP_MOD"; - case OP_LSHIFT : return "OP_LSHIFT"; - case OP_RSHIFT : return "OP_RSHIFT"; - case OP_BOOLAND : return "OP_BOOLAND"; - case OP_BOOLOR : return "OP_BOOLOR"; - case OP_NUMEQUAL : return "OP_NUMEQUAL"; - case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; - case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; - case OP_LESSTHAN : return "OP_LESSTHAN"; - case OP_GREATERTHAN : return "OP_GREATERTHAN"; - case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; - case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; - case OP_MIN : return "OP_MIN"; - case OP_MAX : return "OP_MAX"; - case OP_WITHIN : return "OP_WITHIN"; - - // crypto - case OP_RIPEMD160 : return "OP_RIPEMD160"; - case OP_SHA1 : return "OP_SHA1"; - case OP_SHA256 : return "OP_SHA256"; - case OP_HASH160 : return "OP_HASH160"; - case OP_HASH256 : return "OP_HASH256"; - case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; - case OP_CHECKSIG : return "OP_CHECKSIG"; - case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; - case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; - case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - - // expanson - case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; - case OP_NOP3 : return "OP_NOP3"; - case OP_NOP4 : return "OP_NOP4"; - case OP_NOP5 : return "OP_NOP5"; - case OP_NOP6 : return "OP_NOP6"; - case OP_NOP7 : return "OP_NOP7"; - case OP_NOP8 : return "OP_NOP8"; - case OP_NOP9 : return "OP_NOP9"; - case OP_NOP10 : return "OP_NOP10"; - - case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; - - // Note: - // The template matching params OP_SMALLDATA/etc are defined in opcodetype enum - // as kind of implementation hack, they are *NOT* real opcodes. If found in real - // Script, just let the default: case deal with them. - - default: - return "OP_UNKNOWN"; - } -} - bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { if (!(flags & SCRIPT_VERIFY_STRICTENC)) return true; @@ -240,7 +69,7 @@ bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { if (vchPubKey.size() != 33) return error("Non-canonical public key: invalid length for compressed key"); } else { - return error("Non-canonical public key: compressed nor uncompressed"); + return error("Non-canonical public key: neither compressed nor uncompressed"); } return true; } @@ -964,19 +793,12 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co return false; } - if (!vfExec.empty()) return false; return true; } - - - - - - namespace { /** Wrapper that serializes like CTransaction, but with the modifications @@ -1153,8 +975,7 @@ public: } }; -bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, - const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) +bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags) { static CSignatureCache signatureCache; @@ -1185,426 +1006,6 @@ bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubK return true; } - - - - - - - - -// -// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. -// -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet) -{ - // Templates - static multimap<txnouttype, CScript> mTemplates; - if (mTemplates.empty()) - { - // Standard tx, sender provides pubkey, receiver adds signature - mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); - - // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); - - // Sender provides N pubkeys, receivers provides M signatures - mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); - - // Empty, provably prunable, data-carrying output - if (GetBoolArg("-datacarrier", true)) - mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); - mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); - } - - // Shortcut for pay-to-script-hash, which are more constrained than the other types: - // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL - if (scriptPubKey.IsPayToScriptHash()) - { - typeRet = TX_SCRIPTHASH; - vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); - vSolutionsRet.push_back(hashBytes); - return true; - } - - // Scan templates - const CScript& script1 = scriptPubKey; - BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) - { - const CScript& script2 = tplate.second; - vSolutionsRet.clear(); - - opcodetype opcode1, opcode2; - vector<unsigned char> vch1, vch2; - - // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - while (true) - { - if (pc1 == script1.end() && pc2 == script2.end()) - { - // Found a match - typeRet = tplate.first; - if (typeRet == TX_MULTISIG) - { - // Additional checks for TX_MULTISIG: - unsigned char m = vSolutionsRet.front()[0]; - unsigned char n = vSolutionsRet.back()[0]; - if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) - return false; - } - return true; - } - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - - // Template matching opcodes: - if (opcode2 == OP_PUBKEYS) - { - while (vch1.size() >= 33 && vch1.size() <= 65) - { - vSolutionsRet.push_back(vch1); - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - } - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - // Normal situation is to fall through - // to other if/else statements - } - - if (opcode2 == OP_PUBKEY) - { - if (vch1.size() < 33 || vch1.size() > 65) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_PUBKEYHASH) - { - if (vch1.size() != sizeof(uint160)) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_SMALLINTEGER) - { // Single-byte small integer pushed onto vSolutions - if (opcode1 == OP_0 || - (opcode1 >= OP_1 && opcode1 <= OP_16)) - { - char n = (char)CScript::DecodeOP_N(opcode1); - vSolutionsRet.push_back(valtype(1, n)); - } - else - break; - } - else if (opcode2 == OP_SMALLDATA) - { - // small pushdata, <= MAX_OP_RETURN_RELAY bytes - if (vch1.size() > MAX_OP_RETURN_RELAY) - break; - } - else if (opcode1 != opcode2 || vch1 != vch2) - { - // Others must match exactly - break; - } - } - } - - vSolutionsRet.clear(); - typeRet = TX_NONSTANDARD; - return false; -} - - -bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) -{ - CKey key; - if (!keystore.GetKey(address, key)) - return false; - - vector<unsigned char> vchSig; - if (!key.Sign(hash, vchSig)) - return false; - vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig; - - return true; -} - -bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) -{ - int nSigned = 0; - int nRequired = multisigdata.front()[0]; - for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) - { - const valtype& pubkey = multisigdata[i]; - CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) - ++nSigned; - } - return nSigned==nRequired; -} - -// -// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. -// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), -// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. -// Returns false if scriptPubKey could not be completely satisfied. -// -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, - CScript& scriptSigRet, txnouttype& whichTypeRet) -{ - scriptSigRet.clear(); - - vector<valtype> vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) - return false; - - CKeyID keyID; - switch (whichTypeRet) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return false; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) - return false; - else - { - CPubKey vch; - keystore.GetPubKey(keyID, vch); - scriptSigRet << vch; - } - return true; - case TX_SCRIPTHASH: - return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); - - case TX_MULTISIG: - scriptSigRet << OP_0; // workaround CHECKMULTISIG bug - return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); - } - return false; -} - -int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions) -{ - switch (t) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return -1; - case TX_PUBKEY: - return 1; - case TX_PUBKEYHASH: - return 2; - case TX_MULTISIG: - if (vSolutions.size() < 1 || vSolutions[0].size() < 1) - return -1; - return vSolutions[0][0] + 1; - case TX_SCRIPTHASH: - return 1; // doesn't include args needed by the script - } - return -1; -} - -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) -{ - vector<valtype> vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_MULTISIG) - { - unsigned char m = vSolutions.front()[0]; - unsigned char n = vSolutions.back()[0]; - // Support up to x-of-3 multisig txns as standard - if (n < 1 || n > 3) - return false; - if (m < 1 || m > n) - return false; - } - - return whichType != TX_NONSTANDARD; -} - - -unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore) -{ - unsigned int nResult = 0; - BOOST_FOREACH(const valtype& pubkey, pubkeys) - { - CKeyID keyID = CPubKey(pubkey).GetID(); - if (keystore.HaveKey(keyID)) - ++nResult; - } - return nResult; -} - -isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) -{ - CScript script; - script.SetDestination(dest); - return IsMine(keystore, script); -} - -isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) -{ - vector<valtype> vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_ONLY; - return ISMINE_NO; - } - - CKeyID keyID; - switch (whichType) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - break; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; - break; - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; - break; - case TX_SCRIPTHASH: - { - CScriptID scriptID = CScriptID(uint160(vSolutions[0])); - CScript subscript; - if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) - return ret; - } - break; - } - case TX_MULTISIG: - { - // Only consider transactions "mine" if we own ALL the - // keys involved. multi-signature transactions that are - // partially owned (somebody else has a key that can spend - // them) enable spend-out-from-under-you attacks, especially - // in shared-wallet situations. - vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - if (HaveKeys(keys, keystore) == keys.size()) - return ISMINE_SPENDABLE; - break; - } - } - - if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_ONLY; - return ISMINE_NO; -} - -bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) -{ - vector<valtype> vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_PUBKEY) - { - addressRet = CPubKey(vSolutions[0]).GetID(); - return true; - } - else if (whichType == TX_PUBKEYHASH) - { - addressRet = CKeyID(uint160(vSolutions[0])); - return true; - } - else if (whichType == TX_SCRIPTHASH) - { - addressRet = CScriptID(uint160(vSolutions[0])); - return true; - } - // Multisig txns have more than one address... - return false; -} - -bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet) -{ - addressRet.clear(); - typeRet = TX_NONSTANDARD; - vector<valtype> vSolutions; - if (!Solver(scriptPubKey, typeRet, vSolutions)) - return false; - if (typeRet == TX_NULL_DATA){ - // This is data, not addresses - return false; - } - - if (typeRet == TX_MULTISIG) - { - nRequiredRet = vSolutions.front()[0]; - for (unsigned int i = 1; i < vSolutions.size()-1; i++) - { - CTxDestination address = CPubKey(vSolutions[i]).GetID(); - addressRet.push_back(address); - } - } - else - { - nRequiredRet = 1; - CTxDestination address; - if (!ExtractDestination(scriptPubKey, address)) - return false; - addressRet.push_back(address); - } - - return true; -} - -class CAffectedKeysVisitor : public boost::static_visitor<void> { -private: - const CKeyStore &keystore; - std::vector<CKeyID> &vKeys; - -public: - CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} - - void Process(const CScript &script) { - txnouttype type; - std::vector<CTxDestination> vDest; - int nRequired; - if (ExtractDestinations(script, type, vDest, nRequired)) { - BOOST_FOREACH(const CTxDestination &dest, vDest) - boost::apply_visitor(*this, dest); - } - } - - void operator()(const CKeyID &keyId) { - if (keystore.HaveKey(keyId)) - vKeys.push_back(keyId); - } - - void operator()(const CScriptID &scriptId) { - CScript script; - if (keystore.GetCScript(scriptId, script)) - Process(script); - } - - void operator()(const CNoDestination &none) {} -}; - -void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys) { - CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey); -} - bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { @@ -1645,437 +1046,3 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return true; } - - -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - - // Leave out the signature from the hash, since a signature can't sign itself. - // The checksig op will also drop the signatures from its hash. - uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); - - txnouttype whichType; - if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) - return false; - - if (whichType == TX_SCRIPTHASH) - { - // Solver returns the subscript that need to be evaluated; - // the final scriptSig is the signatures from that - // and then the serialized subscript: - CScript subscript = txin.scriptSig; - - // Recompute txn hash using subscript in place of scriptPubKey: - uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); - - txnouttype subType; - bool fSolved = - Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; - // Append serialized subscript whether or not it is completely signed: - txin.scriptSig << static_cast<valtype>(subscript); - if (!fSolved) return false; - } - - // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS, 0); -} - -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); -} - -static CScript PushAll(const vector<valtype>& values) -{ - CScript result; - BOOST_FOREACH(const valtype& v, values) - result << v; - return result; -} - -static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& txTo, unsigned int nIn, - const vector<valtype>& vSolutions, - vector<valtype>& sigs1, vector<valtype>& sigs2) -{ - // Combine all the signatures we've got: - set<valtype> allsigs; - BOOST_FOREACH(const valtype& v, sigs1) - { - if (!v.empty()) - allsigs.insert(v); - } - BOOST_FOREACH(const valtype& v, sigs2) - { - if (!v.empty()) - allsigs.insert(v); - } - - // Build a map of pubkey -> signature by matching sigs to pubkeys: - assert(vSolutions.size() > 1); - unsigned int nSigsRequired = vSolutions.front()[0]; - unsigned int nPubKeys = vSolutions.size()-2; - map<valtype, valtype> sigs; - BOOST_FOREACH(const valtype& sig, allsigs) - { - for (unsigned int i = 0; i < nPubKeys; i++) - { - const valtype& pubkey = vSolutions[i+1]; - if (sigs.count(pubkey)) - continue; // Already got a sig for this pubkey - - if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) - { - sigs[pubkey] = sig; - break; - } - } - } - // Now build a merged CScript: - unsigned int nSigsHave = 0; - CScript result; result << OP_0; // pop-one-too-many workaround - for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) - { - if (sigs.count(vSolutions[i+1])) - { - result << sigs[vSolutions[i+1]]; - ++nSigsHave; - } - } - // Fill any missing with OP_0: - for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result << OP_0; - - return result; -} - -static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const txnouttype txType, const vector<valtype>& vSolutions, - vector<valtype>& sigs1, vector<valtype>& sigs2) -{ - switch (txType) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.size() >= sigs2.size()) - return PushAll(sigs1); - return PushAll(sigs2); - case TX_PUBKEY: - case TX_PUBKEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.empty() || sigs1[0].empty()) - return PushAll(sigs2); - return PushAll(sigs1); - case TX_SCRIPTHASH: - if (sigs1.empty() || sigs1.back().empty()) - return PushAll(sigs2); - else if (sigs2.empty() || sigs2.back().empty()) - return PushAll(sigs1); - else - { - // Recur to combine: - valtype spk = sigs1.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - vector<vector<unsigned char> > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.pop_back(); - sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); - result << spk; - return result; - } - case TX_MULTISIG: - return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); - } - - return CScript(); -} - -CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const CScript& scriptSig1, const CScript& scriptSig2) -{ - txnouttype txType; - vector<vector<unsigned char> > vSolutions; - Solver(scriptPubKey, txType, vSolutions); - - vector<valtype> stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); - vector<valtype> stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); - - return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); -} - -unsigned int CScript::GetSigOpCount(bool fAccurate) const -{ - unsigned int n = 0; - const_iterator pc = begin(); - opcodetype lastOpcode = OP_INVALIDOPCODE; - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - break; - if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) - n++; - else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) - { - if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) - n += DecodeOP_N(lastOpcode); - else - n += 20; - } - lastOpcode = opcode; - } - return n; -} - -unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const -{ - if (!IsPayToScriptHash()) - return GetSigOpCount(true); - - // This is a pay-to-script-hash scriptPubKey; - // get the last item that the scriptSig - // pushes onto the stack: - const_iterator pc = scriptSig.begin(); - vector<unsigned char> data; - while (pc < scriptSig.end()) - { - opcodetype opcode; - if (!scriptSig.GetOp(pc, opcode, data)) - return 0; - if (opcode > OP_16) - return 0; - } - - /// ... and return its opcount: - CScript subscript(data.begin(), data.end()); - return subscript.GetSigOpCount(true); -} - -bool CScript::IsPayToScriptHash() const -{ - // Extra-fast test for pay-to-script-hash CScripts: - return (this->size() == 23 && - this->at(0) == OP_HASH160 && - this->at(1) == 0x14 && - this->at(22) == OP_EQUAL); -} - -bool CScript::IsPushOnly() const -{ - const_iterator pc = begin(); - while (pc < end()) - { - // Note how a script with an invalid PUSHDATA returns False. - opcodetype opcode; - if (!GetOp(pc, opcode)) - return false; - - // Note that IsPushOnly() *does* consider OP_RESERVED to be a - // push-type opcode, however execution of OP_RESERVED fails, so - // it's not relevant to P2SH as the scriptSig would fail prior to - // the P2SH special validation code being executed. - if (opcode > OP_16) - return false; - } - return true; -} - -bool CScript::HasCanonicalPushes() const -{ - const_iterator pc = begin(); - while (pc < end()) - { - opcodetype opcode; - std::vector<unsigned char> data; - if (!GetOp(pc, opcode, data)) - return false; - if (opcode > OP_16) - continue; - if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) - // Could have used an OP_n code, rather than a 1-byte push. - return false; - if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) - // Could have used a normal n-byte push, rather than OP_PUSHDATA1. - return false; - if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) - // Could have used an OP_PUSHDATA1. - return false; - if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) - // Could have used an OP_PUSHDATA2. - return false; - } - return true; -} - -class CScriptVisitor : public boost::static_visitor<bool> -{ -private: - CScript *script; -public: - CScriptVisitor(CScript *scriptin) { script = scriptin; } - - bool operator()(const CNoDestination &dest) const { - script->clear(); - return false; - } - - bool operator()(const CKeyID &keyID) const { - script->clear(); - *script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; - return true; - } - - bool operator()(const CScriptID &scriptID) const { - script->clear(); - *script << OP_HASH160 << scriptID << OP_EQUAL; - return true; - } -}; - -void CScript::SetDestination(const CTxDestination& dest) -{ - boost::apply_visitor(CScriptVisitor(this), dest); -} - -void CScript::SetMultisig(int nRequired, const std::vector<CPubKey>& keys) -{ - this->clear(); - - *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CPubKey& key, keys) - *this << key; - *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; -} - -bool CScriptCompressor::IsToKeyID(CKeyID &hash) const -{ - if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 - && script[2] == 20 && script[23] == OP_EQUALVERIFY - && script[24] == OP_CHECKSIG) { - memcpy(&hash, &script[3], 20); - return true; - } - return false; -} - -bool CScriptCompressor::IsToScriptID(CScriptID &hash) const -{ - if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20 - && script[22] == OP_EQUAL) { - memcpy(&hash, &script[2], 20); - return true; - } - return false; -} - -bool CScriptCompressor::IsToPubKey(CPubKey &pubkey) const -{ - if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG - && (script[1] == 0x02 || script[1] == 0x03)) { - pubkey.Set(&script[1], &script[34]); - return true; - } - if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG - && script[1] == 0x04) { - pubkey.Set(&script[1], &script[66]); - return pubkey.IsFullyValid(); // if not fully valid, a case that would not be compressible - } - return false; -} - -bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const -{ - CKeyID keyID; - if (IsToKeyID(keyID)) { - out.resize(21); - out[0] = 0x00; - memcpy(&out[1], &keyID, 20); - return true; - } - CScriptID scriptID; - if (IsToScriptID(scriptID)) { - out.resize(21); - out[0] = 0x01; - memcpy(&out[1], &scriptID, 20); - return true; - } - CPubKey pubkey; - if (IsToPubKey(pubkey)) { - out.resize(33); - memcpy(&out[1], &pubkey[1], 32); - if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { - out[0] = pubkey[0]; - return true; - } else if (pubkey[0] == 0x04) { - out[0] = 0x04 | (pubkey[64] & 0x01); - return true; - } - } - return false; -} - -unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const -{ - if (nSize == 0 || nSize == 1) - return 20; - if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5) - return 32; - return 0; -} - -bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigned char> &in) -{ - switch(nSize) { - case 0x00: - script.resize(25); - script[0] = OP_DUP; - script[1] = OP_HASH160; - script[2] = 20; - memcpy(&script[3], &in[0], 20); - script[23] = OP_EQUALVERIFY; - script[24] = OP_CHECKSIG; - return true; - case 0x01: - script.resize(23); - script[0] = OP_HASH160; - script[1] = 20; - memcpy(&script[2], &in[0], 20); - script[22] = OP_EQUAL; - return true; - case 0x02: - case 0x03: - script.resize(35); - script[0] = 33; - script[1] = nSize; - memcpy(&script[2], &in[0], 32); - script[34] = OP_CHECKSIG; - return true; - case 0x04: - case 0x05: - unsigned char vch[33] = {}; - vch[0] = nSize - 2; - memcpy(&vch[1], &in[0], 32); - CPubKey pubkey(&vch[0], &vch[33]); - if (!pubkey.Decompress()) - return false; - assert(pubkey.size() == 65); - script.resize(67); - script[0] = 65; - memcpy(&script[1], pubkey.begin(), 65); - script[66] = OP_CHECKSIG; - return true; - } - return false; -} diff --git a/src/script/interpreter.h b/src/script/interpreter.h new file mode 100644 index 0000000000..0c6f8b9d13 --- /dev/null +++ b/src/script/interpreter.h @@ -0,0 +1,45 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPT_INTERPRETER +#define H_BITCOIN_SCRIPT_INTERPRETER + +#include <vector> +#include <stdint.h> +#include <string> + +class uint256; +class CScript; +class CTransaction; + +/** Signature hash types/flags */ +enum +{ + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_ANYONECANPAY = 0x80, +}; + +/** Script verification flags */ +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); + +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 nHashType, int flags); +bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); + +#endif diff --git a/src/script/script.cpp b/src/script/script.cpp new file mode 100644 index 0000000000..60d1beac95 --- /dev/null +++ b/src/script/script.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "script.h" + +#include <boost/foreach.hpp> + +using namespace std; + +const char* GetOpName(opcodetype opcode) +{ + switch (opcode) + { + // push value + case OP_0 : return "0"; + case OP_PUSHDATA1 : return "OP_PUSHDATA1"; + case OP_PUSHDATA2 : return "OP_PUSHDATA2"; + case OP_PUSHDATA4 : return "OP_PUSHDATA4"; + case OP_1NEGATE : return "-1"; + case OP_RESERVED : return "OP_RESERVED"; + case OP_1 : return "1"; + case OP_2 : return "2"; + case OP_3 : return "3"; + case OP_4 : return "4"; + case OP_5 : return "5"; + case OP_6 : return "6"; + case OP_7 : return "7"; + case OP_8 : return "8"; + case OP_9 : return "9"; + case OP_10 : return "10"; + case OP_11 : return "11"; + case OP_12 : return "12"; + case OP_13 : return "13"; + case OP_14 : return "14"; + case OP_15 : return "15"; + case OP_16 : return "16"; + + // control + case OP_NOP : return "OP_NOP"; + case OP_VER : return "OP_VER"; + case OP_IF : return "OP_IF"; + case OP_NOTIF : return "OP_NOTIF"; + case OP_VERIF : return "OP_VERIF"; + case OP_VERNOTIF : return "OP_VERNOTIF"; + case OP_ELSE : return "OP_ELSE"; + case OP_ENDIF : return "OP_ENDIF"; + case OP_VERIFY : return "OP_VERIFY"; + case OP_RETURN : return "OP_RETURN"; + + // stack ops + case OP_TOALTSTACK : return "OP_TOALTSTACK"; + case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; + case OP_2DROP : return "OP_2DROP"; + case OP_2DUP : return "OP_2DUP"; + case OP_3DUP : return "OP_3DUP"; + case OP_2OVER : return "OP_2OVER"; + case OP_2ROT : return "OP_2ROT"; + case OP_2SWAP : return "OP_2SWAP"; + case OP_IFDUP : return "OP_IFDUP"; + case OP_DEPTH : return "OP_DEPTH"; + case OP_DROP : return "OP_DROP"; + case OP_DUP : return "OP_DUP"; + case OP_NIP : return "OP_NIP"; + case OP_OVER : return "OP_OVER"; + case OP_PICK : return "OP_PICK"; + case OP_ROLL : return "OP_ROLL"; + case OP_ROT : return "OP_ROT"; + case OP_SWAP : return "OP_SWAP"; + case OP_TUCK : return "OP_TUCK"; + + // splice ops + case OP_CAT : return "OP_CAT"; + case OP_SUBSTR : return "OP_SUBSTR"; + case OP_LEFT : return "OP_LEFT"; + case OP_RIGHT : return "OP_RIGHT"; + case OP_SIZE : return "OP_SIZE"; + + // bit logic + case OP_INVERT : return "OP_INVERT"; + case OP_AND : return "OP_AND"; + case OP_OR : return "OP_OR"; + case OP_XOR : return "OP_XOR"; + case OP_EQUAL : return "OP_EQUAL"; + case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; + case OP_RESERVED1 : return "OP_RESERVED1"; + case OP_RESERVED2 : return "OP_RESERVED2"; + + // numeric + case OP_1ADD : return "OP_1ADD"; + case OP_1SUB : return "OP_1SUB"; + case OP_2MUL : return "OP_2MUL"; + case OP_2DIV : return "OP_2DIV"; + case OP_NEGATE : return "OP_NEGATE"; + case OP_ABS : return "OP_ABS"; + case OP_NOT : return "OP_NOT"; + case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; + case OP_ADD : return "OP_ADD"; + case OP_SUB : return "OP_SUB"; + case OP_MUL : return "OP_MUL"; + case OP_DIV : return "OP_DIV"; + case OP_MOD : return "OP_MOD"; + case OP_LSHIFT : return "OP_LSHIFT"; + case OP_RSHIFT : return "OP_RSHIFT"; + case OP_BOOLAND : return "OP_BOOLAND"; + case OP_BOOLOR : return "OP_BOOLOR"; + case OP_NUMEQUAL : return "OP_NUMEQUAL"; + case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; + case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; + case OP_LESSTHAN : return "OP_LESSTHAN"; + case OP_GREATERTHAN : return "OP_GREATERTHAN"; + case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; + case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; + case OP_MIN : return "OP_MIN"; + case OP_MAX : return "OP_MAX"; + case OP_WITHIN : return "OP_WITHIN"; + + // crypto + case OP_RIPEMD160 : return "OP_RIPEMD160"; + case OP_SHA1 : return "OP_SHA1"; + case OP_SHA256 : return "OP_SHA256"; + case OP_HASH160 : return "OP_HASH160"; + case OP_HASH256 : return "OP_HASH256"; + case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; + case OP_CHECKSIG : return "OP_CHECKSIG"; + case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; + case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; + case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + + // expanson + case OP_NOP1 : return "OP_NOP1"; + case OP_NOP2 : return "OP_NOP2"; + case OP_NOP3 : return "OP_NOP3"; + case OP_NOP4 : return "OP_NOP4"; + case OP_NOP5 : return "OP_NOP5"; + case OP_NOP6 : return "OP_NOP6"; + case OP_NOP7 : return "OP_NOP7"; + case OP_NOP8 : return "OP_NOP8"; + case OP_NOP9 : return "OP_NOP9"; + case OP_NOP10 : return "OP_NOP10"; + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + + // Note: + // The template matching params OP_SMALLDATA/etc are defined in opcodetype enum + // as kind of implementation hack, they are *NOT* real opcodes. If found in real + // Script, just let the default: case deal with them. + + default: + return "OP_UNKNOWN"; + } +} + +unsigned int CScript::GetSigOpCount(bool fAccurate) const +{ + unsigned int n = 0; + const_iterator pc = begin(); + opcodetype lastOpcode = OP_INVALIDOPCODE; + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + break; + if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) + n++; + else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) + { + if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) + n += DecodeOP_N(lastOpcode); + else + n += 20; + } + lastOpcode = opcode; + } + return n; +} + +unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const +{ + if (!IsPayToScriptHash()) + return GetSigOpCount(true); + + // This is a pay-to-script-hash scriptPubKey; + // get the last item that the scriptSig + // pushes onto the stack: + const_iterator pc = scriptSig.begin(); + vector<unsigned char> data; + while (pc < scriptSig.end()) + { + opcodetype opcode; + if (!scriptSig.GetOp(pc, opcode, data)) + return 0; + if (opcode > OP_16) + return 0; + } + + /// ... and return its opcount: + CScript subscript(data.begin(), data.end()); + return subscript.GetSigOpCount(true); +} + +bool CScript::IsPayToScriptHash() const +{ + // Extra-fast test for pay-to-script-hash CScripts: + return (this->size() == 23 && + this->at(0) == OP_HASH160 && + this->at(1) == 0x14 && + this->at(22) == OP_EQUAL); +} + +bool CScript::IsPushOnly() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + // Note that IsPushOnly() *does* consider OP_RESERVED to be a + // push-type opcode, however execution of OP_RESERVED fails, so + // it's not relevant to P2SH as the scriptSig would fail prior to + // the P2SH special validation code being executed. + if (opcode > OP_16) + return false; + } + return true; +} + +bool CScript::HasCanonicalPushes() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector<unsigned char> data; + if (!GetOp(pc, opcode, data)) + return false; + if (opcode > OP_16) + continue; + if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) + // Could have used an OP_n code, rather than a 1-byte push. + return false; + if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) + // Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) + // Could have used an OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) + // Could have used an OP_PUSHDATA2. + return false; + } + return true; +} + +class CScriptVisitor : public boost::static_visitor<bool> +{ +private: + CScript *script; +public: + CScriptVisitor(CScript *scriptin) { script = scriptin; } + + bool operator()(const CNoDestination &dest) const { + script->clear(); + return false; + } + + bool operator()(const CKeyID &keyID) const { + script->clear(); + *script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; + return true; + } + + bool operator()(const CScriptID &scriptID) const { + script->clear(); + *script << OP_HASH160 << scriptID << OP_EQUAL; + return true; + } +}; + +void CScript::SetDestination(const CTxDestination& dest) +{ + boost::apply_visitor(CScriptVisitor(this), dest); +} + +void CScript::SetMultisig(int nRequired, const std::vector<CPubKey>& keys) +{ + this->clear(); + + *this << EncodeOP_N(nRequired); + BOOST_FOREACH(const CPubKey& key, keys) + *this << key; + *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; +} diff --git a/src/script.h b/src/script/script.h index d17cfe3fa4..21847c09bd 100644 --- a/src/script.h +++ b/src/script/script.h @@ -7,22 +7,162 @@ #define H_BITCOIN_SCRIPT #include "key.h" -#include "utilstrencodings.h" #include "tinyformat.h" +#include "utilstrencodings.h" #include <stdexcept> -#include <stdint.h> -#include <string> -#include <vector> #include <boost/variant.hpp> -class CKeyStore; -class CTransaction; -struct CMutableTransaction; - static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes -static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes + +/** Script opcodes */ +enum opcodetype +{ + // push value + OP_0 = 0x00, + OP_FALSE = OP_0, + OP_PUSHDATA1 = 0x4c, + OP_PUSHDATA2 = 0x4d, + OP_PUSHDATA4 = 0x4e, + OP_1NEGATE = 0x4f, + OP_RESERVED = 0x50, + OP_1 = 0x51, + OP_TRUE=OP_1, + OP_2 = 0x52, + OP_3 = 0x53, + OP_4 = 0x54, + OP_5 = 0x55, + OP_6 = 0x56, + OP_7 = 0x57, + OP_8 = 0x58, + OP_9 = 0x59, + OP_10 = 0x5a, + OP_11 = 0x5b, + OP_12 = 0x5c, + OP_13 = 0x5d, + OP_14 = 0x5e, + OP_15 = 0x5f, + OP_16 = 0x60, + + // control + OP_NOP = 0x61, + OP_VER = 0x62, + OP_IF = 0x63, + OP_NOTIF = 0x64, + OP_VERIF = 0x65, + OP_VERNOTIF = 0x66, + OP_ELSE = 0x67, + OP_ENDIF = 0x68, + OP_VERIFY = 0x69, + OP_RETURN = 0x6a, + + // stack ops + OP_TOALTSTACK = 0x6b, + OP_FROMALTSTACK = 0x6c, + OP_2DROP = 0x6d, + OP_2DUP = 0x6e, + OP_3DUP = 0x6f, + OP_2OVER = 0x70, + OP_2ROT = 0x71, + OP_2SWAP = 0x72, + OP_IFDUP = 0x73, + OP_DEPTH = 0x74, + OP_DROP = 0x75, + OP_DUP = 0x76, + OP_NIP = 0x77, + OP_OVER = 0x78, + OP_PICK = 0x79, + OP_ROLL = 0x7a, + OP_ROT = 0x7b, + OP_SWAP = 0x7c, + OP_TUCK = 0x7d, + + // splice ops + OP_CAT = 0x7e, + OP_SUBSTR = 0x7f, + OP_LEFT = 0x80, + OP_RIGHT = 0x81, + OP_SIZE = 0x82, + + // bit logic + OP_INVERT = 0x83, + OP_AND = 0x84, + OP_OR = 0x85, + OP_XOR = 0x86, + OP_EQUAL = 0x87, + OP_EQUALVERIFY = 0x88, + OP_RESERVED1 = 0x89, + OP_RESERVED2 = 0x8a, + + // numeric + OP_1ADD = 0x8b, + OP_1SUB = 0x8c, + OP_2MUL = 0x8d, + OP_2DIV = 0x8e, + OP_NEGATE = 0x8f, + OP_ABS = 0x90, + OP_NOT = 0x91, + OP_0NOTEQUAL = 0x92, + + OP_ADD = 0x93, + OP_SUB = 0x94, + OP_MUL = 0x95, + OP_DIV = 0x96, + OP_MOD = 0x97, + OP_LSHIFT = 0x98, + OP_RSHIFT = 0x99, + + OP_BOOLAND = 0x9a, + OP_BOOLOR = 0x9b, + OP_NUMEQUAL = 0x9c, + OP_NUMEQUALVERIFY = 0x9d, + OP_NUMNOTEQUAL = 0x9e, + OP_LESSTHAN = 0x9f, + OP_GREATERTHAN = 0xa0, + OP_LESSTHANOREQUAL = 0xa1, + OP_GREATERTHANOREQUAL = 0xa2, + OP_MIN = 0xa3, + OP_MAX = 0xa4, + + OP_WITHIN = 0xa5, + + // crypto + OP_RIPEMD160 = 0xa6, + OP_SHA1 = 0xa7, + OP_SHA256 = 0xa8, + OP_HASH160 = 0xa9, + OP_HASH256 = 0xaa, + OP_CODESEPARATOR = 0xab, + OP_CHECKSIG = 0xac, + OP_CHECKSIGVERIFY = 0xad, + OP_CHECKMULTISIG = 0xae, + OP_CHECKMULTISIGVERIFY = 0xaf, + + // expansion + OP_NOP1 = 0xb0, + OP_NOP2 = 0xb1, + OP_NOP3 = 0xb2, + OP_NOP4 = 0xb3, + OP_NOP5 = 0xb4, + OP_NOP6 = 0xb5, + OP_NOP7 = 0xb6, + OP_NOP8 = 0xb7, + OP_NOP9 = 0xb8, + OP_NOP10 = 0xb9, + + + // template matching params + OP_SMALLDATA = 0xf9, + OP_SMALLINTEGER = 0xfa, + OP_PUBKEYS = 0xfb, + OP_PUBKEYHASH = 0xfd, + OP_PUBKEY = 0xfe, + + OP_INVALIDOPCODE = 0xff, +}; + +const char* GetOpName(opcodetype opcode); class scriptnum_error : public std::runtime_error { @@ -131,7 +271,6 @@ public: absvalue >>= 8; } - // - If the most significant byte is >= 0x80 and the value is positive, push a // new zero-byte to make the significant byte < 0x80 again. @@ -173,66 +312,13 @@ private: int64_t m_value; }; -/** Signature hash types/flags */ -enum -{ - SIGHASH_ALL = 1, - SIGHASH_NONE = 2, - SIGHASH_SINGLE = 3, - SIGHASH_ANYONECANPAY = 0x80, -}; - -/** Script verification flags */ -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 -}; - -/** IsMine() return codes */ -enum isminetype -{ - ISMINE_NO = 0, - ISMINE_WATCH_ONLY = 1, - ISMINE_SPENDABLE = 2, - ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE -}; -/** used for bitflags of isminetype */ -typedef uint8_t isminefilter; - -// Mandatory script verification flags that all new blocks must comply with for -// them to be valid. (but old blocks may not comply with) Currently just P2SH, -// but in the future other flags may be added, such as a soft-fork to enforce -// strict DER encoding. -// -// Failing one of these tests may trigger a DoS ban - see CheckInputs() for -// details. -static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; - -// Standard script verification flags that standard transactions will comply -// with. However scripts violating these flags may still be present in valid -// blocks and we must accept those blocks. -static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | - SCRIPT_VERIFY_STRICTENC | - SCRIPT_VERIFY_NULLDUMMY; - -// For convenience, standard but not mandatory verify flags. -static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; - -enum txnouttype +inline std::string ValueString(const std::vector<unsigned char>& vch) { - TX_NONSTANDARD, - // 'standard' transaction types: - TX_PUBKEY, - TX_PUBKEYHASH, - TX_SCRIPTHASH, - TX_MULTISIG, - TX_NULL_DATA, -}; + if (vch.size() <= 4) + return strprintf("%d", CScriptNum(vch).getint()); + else + return HexStr(vch); +} class CNoDestination { public: @@ -248,167 +334,6 @@ public: */ typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination; -const char* GetTxnOutputType(txnouttype t); - -/** Script opcodes */ -enum opcodetype -{ - // push value - OP_0 = 0x00, - OP_FALSE = OP_0, - OP_PUSHDATA1 = 0x4c, - OP_PUSHDATA2 = 0x4d, - OP_PUSHDATA4 = 0x4e, - OP_1NEGATE = 0x4f, - OP_RESERVED = 0x50, - OP_1 = 0x51, - OP_TRUE=OP_1, - OP_2 = 0x52, - OP_3 = 0x53, - OP_4 = 0x54, - OP_5 = 0x55, - OP_6 = 0x56, - OP_7 = 0x57, - OP_8 = 0x58, - OP_9 = 0x59, - OP_10 = 0x5a, - OP_11 = 0x5b, - OP_12 = 0x5c, - OP_13 = 0x5d, - OP_14 = 0x5e, - OP_15 = 0x5f, - OP_16 = 0x60, - - // control - OP_NOP = 0x61, - OP_VER = 0x62, - OP_IF = 0x63, - OP_NOTIF = 0x64, - OP_VERIF = 0x65, - OP_VERNOTIF = 0x66, - OP_ELSE = 0x67, - OP_ENDIF = 0x68, - OP_VERIFY = 0x69, - OP_RETURN = 0x6a, - - // stack ops - OP_TOALTSTACK = 0x6b, - OP_FROMALTSTACK = 0x6c, - OP_2DROP = 0x6d, - OP_2DUP = 0x6e, - OP_3DUP = 0x6f, - OP_2OVER = 0x70, - OP_2ROT = 0x71, - OP_2SWAP = 0x72, - OP_IFDUP = 0x73, - OP_DEPTH = 0x74, - OP_DROP = 0x75, - OP_DUP = 0x76, - OP_NIP = 0x77, - OP_OVER = 0x78, - OP_PICK = 0x79, - OP_ROLL = 0x7a, - OP_ROT = 0x7b, - OP_SWAP = 0x7c, - OP_TUCK = 0x7d, - - // splice ops - OP_CAT = 0x7e, - OP_SUBSTR = 0x7f, - OP_LEFT = 0x80, - OP_RIGHT = 0x81, - OP_SIZE = 0x82, - - // bit logic - OP_INVERT = 0x83, - OP_AND = 0x84, - OP_OR = 0x85, - OP_XOR = 0x86, - OP_EQUAL = 0x87, - OP_EQUALVERIFY = 0x88, - OP_RESERVED1 = 0x89, - OP_RESERVED2 = 0x8a, - - // numeric - OP_1ADD = 0x8b, - OP_1SUB = 0x8c, - OP_2MUL = 0x8d, - OP_2DIV = 0x8e, - OP_NEGATE = 0x8f, - OP_ABS = 0x90, - OP_NOT = 0x91, - OP_0NOTEQUAL = 0x92, - - OP_ADD = 0x93, - OP_SUB = 0x94, - OP_MUL = 0x95, - OP_DIV = 0x96, - OP_MOD = 0x97, - OP_LSHIFT = 0x98, - OP_RSHIFT = 0x99, - - OP_BOOLAND = 0x9a, - OP_BOOLOR = 0x9b, - OP_NUMEQUAL = 0x9c, - OP_NUMEQUALVERIFY = 0x9d, - OP_NUMNOTEQUAL = 0x9e, - OP_LESSTHAN = 0x9f, - OP_GREATERTHAN = 0xa0, - OP_LESSTHANOREQUAL = 0xa1, - OP_GREATERTHANOREQUAL = 0xa2, - OP_MIN = 0xa3, - OP_MAX = 0xa4, - - OP_WITHIN = 0xa5, - - // crypto - OP_RIPEMD160 = 0xa6, - OP_SHA1 = 0xa7, - OP_SHA256 = 0xa8, - OP_HASH160 = 0xa9, - OP_HASH256 = 0xaa, - OP_CODESEPARATOR = 0xab, - OP_CHECKSIG = 0xac, - OP_CHECKSIGVERIFY = 0xad, - OP_CHECKMULTISIG = 0xae, - OP_CHECKMULTISIGVERIFY = 0xaf, - - // expansion - OP_NOP1 = 0xb0, - OP_NOP2 = 0xb1, - OP_NOP3 = 0xb2, - OP_NOP4 = 0xb3, - OP_NOP5 = 0xb4, - OP_NOP6 = 0xb5, - OP_NOP7 = 0xb6, - OP_NOP8 = 0xb7, - OP_NOP9 = 0xb8, - OP_NOP10 = 0xb9, - - - - // template matching params - OP_SMALLDATA = 0xf9, - OP_SMALLINTEGER = 0xfa, - OP_PUBKEYS = 0xfb, - OP_PUBKEYHASH = 0xfd, - OP_PUBKEY = 0xfe, - - OP_INVALIDOPCODE = 0xff, -}; - -const char* GetOpName(opcodetype opcode); - - - -inline std::string ValueString(const std::vector<unsigned char>& vch) -{ - if (vch.size() <= 4) - return strprintf("%d", CScriptNum(vch).getint()); - else - return HexStr(vch); -} - /** Serialized script, used inside transaction inputs and outputs */ class CScript : public std::vector<unsigned char> { @@ -446,7 +371,6 @@ public: return ret; } - CScript(int64_t b) { operator<<(b); } explicit CScript(opcodetype b) { operator<<(b); } @@ -718,98 +642,4 @@ public: } }; -/** Compact serializer for scripts. - * - * It detects common cases and encodes them much more efficiently. - * 3 special cases are defined: - * * Pay to pubkey hash (encoded as 21 bytes) - * * Pay to script hash (encoded as 21 bytes) - * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) - * - * Other scripts up to 121 bytes require 1 byte + script length. Above - * that, scripts up to 16505 bytes require 2 bytes + script length. - */ -class CScriptCompressor -{ -private: - // make this static for now (there are only 6 special scripts defined) - // this can potentially be extended together with a new nVersion for - // transactions, in which case this value becomes dependent on nVersion - // and nHeight of the enclosing transaction. - static const unsigned int nSpecialScripts = 6; - - CScript &script; -protected: - // These check for scripts for which a special case with a shorter encoding is defined. - // They are implemented separately from the CScript test, as these test for exact byte - // sequence correspondences, and are more strict. For example, IsToPubKey also verifies - // whether the public key is valid (as invalid ones cannot be represented in compressed - // form). - bool IsToKeyID(CKeyID &hash) const; - bool IsToScriptID(CScriptID &hash) const; - bool IsToPubKey(CPubKey &pubkey) const; - - bool Compress(std::vector<unsigned char> &out) const; - unsigned int GetSpecialSize(unsigned int nSize) const; - bool Decompress(unsigned int nSize, const std::vector<unsigned char> &out); -public: - CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - std::vector<unsigned char> compr; - if (Compress(compr)) - return compr.size(); - unsigned int nSize = script.size() + nSpecialScripts; - return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion); - } - - template<typename Stream> - void Serialize(Stream &s, int nType, int nVersion) const { - std::vector<unsigned char> compr; - if (Compress(compr)) { - s << CFlatData(compr); - return; - } - unsigned int nSize = script.size() + nSpecialScripts; - s << VARINT(nSize); - s << CFlatData(script); - } - - template<typename Stream> - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nSize = 0; - s >> VARINT(nSize); - if (nSize < nSpecialScripts) { - std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); - s >> REF(CFlatData(vch)); - Decompress(nSize, vch); - return; - } - nSize -= nSpecialScripts; - script.resize(nSize); - s >> REF(CFlatData(script)); - } -}; - -bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey, unsigned int flags); -bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int flags); - -bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); -int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); -void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); -bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); -bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); - -// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, -// combine them intelligently and return the result. -CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); - -#endif // H_BITCOIN_SCRIPT +#endif diff --git a/src/script/sign.cpp b/src/script/sign.cpp new file mode 100644 index 0000000000..958177de3d --- /dev/null +++ b/src/script/sign.cpp @@ -0,0 +1,260 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "script/sign.h" + +#include "core.h" +#include "key.h" +#include "keystore.h" +#include "script/standard.h" +#include "uint256.h" + +#include <boost/foreach.hpp> + +using namespace std; + +typedef vector<unsigned char> valtype; + +bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + CKey key; + if (!keystore.GetKey(address, key)) + return false; + + vector<unsigned char> vchSig; + if (!key.Sign(hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + + return true; +} + +bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + int nSigned = 0; + int nRequired = multisigdata.front()[0]; + for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) + { + const valtype& pubkey = multisigdata[i]; + CKeyID keyID = CPubKey(pubkey).GetID(); + if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + ++nSigned; + } + return nSigned==nRequired; +} + +// +// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. +// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), +// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. +// Returns false if scriptPubKey could not be completely satisfied. +// +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, + CScript& scriptSigRet, txnouttype& whichTypeRet) +{ + scriptSigRet.clear(); + + vector<valtype> vSolutions; + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) + return false; + + CKeyID keyID; + switch (whichTypeRet) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + return false; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) + return false; + else + { + CPubKey vch; + keystore.GetPubKey(keyID, vch); + scriptSigRet << vch; + } + return true; + case TX_SCRIPTHASH: + return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); + + case TX_MULTISIG: + scriptSigRet << OP_0; // workaround CHECKMULTISIG bug + return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); + } + return false; +} + +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + + // Leave out the signature from the hash, since a signature can't sign itself. + // The checksig op will also drop the signatures from its hash. + uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); + + txnouttype whichType; + if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) + return false; + + if (whichType == TX_SCRIPTHASH) + { + // Solver returns the subscript that need to be evaluated; + // the final scriptSig is the signatures from that + // and then the serialized subscript: + CScript subscript = txin.scriptSig; + + // Recompute txn hash using subscript in place of scriptPubKey: + uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + + txnouttype subType; + bool fSolved = + Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; + // Append serialized subscript whether or not it is completely signed: + txin.scriptSig << static_cast<valtype>(subscript); + if (!fSolved) return false; + } + + // Test solution + return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS, 0); +} + +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); +} + +static CScript PushAll(const vector<valtype>& values) +{ + CScript result; + BOOST_FOREACH(const valtype& v, values) + result << v; + return result; +} + +static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& txTo, unsigned int nIn, + const vector<valtype>& vSolutions, + vector<valtype>& sigs1, vector<valtype>& sigs2) +{ + // Combine all the signatures we've got: + set<valtype> allsigs; + BOOST_FOREACH(const valtype& v, sigs1) + { + if (!v.empty()) + allsigs.insert(v); + } + BOOST_FOREACH(const valtype& v, sigs2) + { + if (!v.empty()) + allsigs.insert(v); + } + + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(vSolutions.size() > 1); + unsigned int nSigsRequired = vSolutions.front()[0]; + unsigned int nPubKeys = vSolutions.size()-2; + map<valtype, valtype> sigs; + BOOST_FOREACH(const valtype& sig, allsigs) + { + for (unsigned int i = 0; i < nPubKeys; i++) + { + const valtype& pubkey = vSolutions[i+1]; + if (sigs.count(pubkey)) + continue; // Already got a sig for this pubkey + + if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) + { + sigs[pubkey] = sig; + break; + } + } + } + // Now build a merged CScript: + unsigned int nSigsHave = 0; + CScript result; result << OP_0; // pop-one-too-many workaround + for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) + { + if (sigs.count(vSolutions[i+1])) + { + result << sigs[vSolutions[i+1]]; + ++nSigsHave; + } + } + // Fill any missing with OP_0: + for (unsigned int i = nSigsHave; i < nSigsRequired; i++) + result << OP_0; + + return result; +} + +static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const txnouttype txType, const vector<valtype>& vSolutions, + vector<valtype>& sigs1, vector<valtype>& sigs2) +{ + switch (txType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.size() >= sigs2.size()) + return PushAll(sigs1); + return PushAll(sigs2); + case TX_PUBKEY: + case TX_PUBKEYHASH: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.empty() || sigs1[0].empty()) + return PushAll(sigs2); + return PushAll(sigs1); + case TX_SCRIPTHASH: + if (sigs1.empty() || sigs1.back().empty()) + return PushAll(sigs2); + else if (sigs2.empty() || sigs2.back().empty()) + return PushAll(sigs1); + else + { + // Recur to combine: + valtype spk = sigs1.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector<vector<unsigned char> > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.pop_back(); + sigs2.pop_back(); + CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); + result << spk; + return result; + } + case TX_MULTISIG: + return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); + } + + return CScript(); +} + +CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, + const CScript& scriptSig1, const CScript& scriptSig2) +{ + txnouttype txType; + vector<vector<unsigned char> > vSolutions; + Solver(scriptPubKey, txType, vSolutions); + + vector<valtype> stack1; + EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + vector<valtype> stack2; + EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); + + return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); +} diff --git a/src/script/sign.h b/src/script/sign.h new file mode 100644 index 0000000000..51723b53af --- /dev/null +++ b/src/script/sign.h @@ -0,0 +1,23 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPT_SIGN +#define H_BITCOIN_SCRIPT_SIGN + +#include "script/interpreter.h" + +class CKeyStore; +class CScript; +class CTransaction; +struct CMutableTransaction; + +bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); + +// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, +// combine them intelligently and return the result. +CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); + +#endif diff --git a/src/script/standard.cpp b/src/script/standard.cpp new file mode 100644 index 0000000000..684edff4d2 --- /dev/null +++ b/src/script/standard.cpp @@ -0,0 +1,254 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "script/standard.h" + +#include "script/script.h" +#include "util.h" + +#include <boost/foreach.hpp> + +using namespace std; + +typedef vector<unsigned char> valtype; + +const char* GetTxnOutputType(txnouttype t) +{ + switch (t) + { + case TX_NONSTANDARD: return "nonstandard"; + case TX_PUBKEY: return "pubkey"; + case TX_PUBKEYHASH: return "pubkeyhash"; + case TX_SCRIPTHASH: return "scripthash"; + case TX_MULTISIG: return "multisig"; + case TX_NULL_DATA: return "nulldata"; + } + return NULL; +} + +// +// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. +// +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet) +{ + // Templates + static multimap<txnouttype, CScript> mTemplates; + if (mTemplates.empty()) + { + // Standard tx, sender provides pubkey, receiver adds signature + mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); + + // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey + mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); + + // Sender provides N pubkeys, receivers provides M signatures + mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + + // Empty, provably prunable, data-carrying output + if (GetBoolArg("-datacarrier", true)) + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); + } + + // Shortcut for pay-to-script-hash, which are more constrained than the other types: + // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL + if (scriptPubKey.IsPayToScriptHash()) + { + typeRet = TX_SCRIPTHASH; + vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); + vSolutionsRet.push_back(hashBytes); + return true; + } + + // Scan templates + const CScript& script1 = scriptPubKey; + BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) + { + const CScript& script2 = tplate.second; + vSolutionsRet.clear(); + + opcodetype opcode1, opcode2; + vector<unsigned char> vch1, vch2; + + // Compare + CScript::const_iterator pc1 = script1.begin(); + CScript::const_iterator pc2 = script2.begin(); + while (true) + { + if (pc1 == script1.end() && pc2 == script2.end()) + { + // Found a match + typeRet = tplate.first; + if (typeRet == TX_MULTISIG) + { + // Additional checks for TX_MULTISIG: + unsigned char m = vSolutionsRet.front()[0]; + unsigned char n = vSolutionsRet.back()[0]; + if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) + return false; + } + return true; + } + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + + // Template matching opcodes: + if (opcode2 == OP_PUBKEYS) + { + while (vch1.size() >= 33 && vch1.size() <= 65) + { + vSolutionsRet.push_back(vch1); + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + } + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + // Normal situation is to fall through + // to other if/else statements + } + + if (opcode2 == OP_PUBKEY) + { + if (vch1.size() < 33 || vch1.size() > 65) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_PUBKEYHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_SMALLINTEGER) + { // Single-byte small integer pushed onto vSolutions + if (opcode1 == OP_0 || + (opcode1 >= OP_1 && opcode1 <= OP_16)) + { + char n = (char)CScript::DecodeOP_N(opcode1); + vSolutionsRet.push_back(valtype(1, n)); + } + else + break; + } + else if (opcode2 == OP_SMALLDATA) + { + // small pushdata, <= MAX_OP_RETURN_RELAY bytes + if (vch1.size() > MAX_OP_RETURN_RELAY) + break; + } + else if (opcode1 != opcode2 || vch1 != vch2) + { + // Others must match exactly + break; + } + } + } + + vSolutionsRet.clear(); + typeRet = TX_NONSTANDARD; + return false; +} + +int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions) +{ + switch (t) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + return -1; + case TX_PUBKEY: + return 1; + case TX_PUBKEYHASH: + return 2; + case TX_MULTISIG: + if (vSolutions.size() < 1 || vSolutions[0].size() < 1) + return -1; + return vSolutions[0][0] + 1; + case TX_SCRIPTHASH: + return 1; // doesn't include args needed by the script + } + return -1; +} + +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) +{ + vector<valtype> vSolutions; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_MULTISIG) + { + unsigned char m = vSolutions.front()[0]; + unsigned char n = vSolutions.back()[0]; + // Support up to x-of-3 multisig txns as standard + if (n < 1 || n > 3) + return false; + if (m < 1 || m > n) + return false; + } + + return whichType != TX_NONSTANDARD; +} + +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) +{ + vector<valtype> vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + addressRet = CPubKey(vSolutions[0]).GetID(); + return true; + } + else if (whichType == TX_PUBKEYHASH) + { + addressRet = CKeyID(uint160(vSolutions[0])); + return true; + } + else if (whichType == TX_SCRIPTHASH) + { + addressRet = CScriptID(uint160(vSolutions[0])); + return true; + } + // Multisig txns have more than one address... + return false; +} + +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet) +{ + addressRet.clear(); + typeRet = TX_NONSTANDARD; + vector<valtype> vSolutions; + if (!Solver(scriptPubKey, typeRet, vSolutions)) + return false; + if (typeRet == TX_NULL_DATA){ + // This is data, not addresses + return false; + } + + if (typeRet == TX_MULTISIG) + { + nRequiredRet = vSolutions.front()[0]; + for (unsigned int i = 1; i < vSolutions.size()-1; i++) + { + CTxDestination address = CPubKey(vSolutions[i]).GetID(); + addressRet.push_back(address); + } + } + else + { + nRequiredRet = 1; + CTxDestination address; + if (!ExtractDestination(scriptPubKey, address)) + return false; + addressRet.push_back(address); + } + + return true; +} diff --git a/src/script/standard.h b/src/script/standard.h new file mode 100644 index 0000000000..18092e879e --- /dev/null +++ b/src/script/standard.h @@ -0,0 +1,56 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPT_STANDARD +#define H_BITCOIN_SCRIPT_STANDARD + +#include "script/script.h" +#include "script/interpreter.h" + +#include <stdint.h> + +class CScript; + +static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes + +// Mandatory script verification flags that all new blocks must comply with for +// them to be valid. (but old blocks may not comply with) Currently just P2SH, +// but in the future other flags may be added, such as a soft-fork to enforce +// strict DER encoding. +// +// Failing one of these tests may trigger a DoS ban - see CheckInputs() for +// details. +static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; + +// Standard script verification flags that standard transactions will comply +// with. However scripts violating these flags may still be present in valid +// blocks and we must accept those blocks. +static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | + SCRIPT_VERIFY_STRICTENC | + SCRIPT_VERIFY_NULLDUMMY; + +// For convenience, standard but not mandatory verify flags. +static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; + +enum txnouttype +{ + TX_NONSTANDARD, + // 'standard' transaction types: + TX_PUBKEY, + TX_PUBKEYHASH, + TX_SCRIPTHASH, + TX_MULTISIG, + TX_NULL_DATA, +}; + +const char* GetTxnOutputType(txnouttype t); + +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); +int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); +bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); +bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); + +#endif diff --git a/src/scriptutils.cpp b/src/scriptutils.cpp new file mode 100644 index 0000000000..a636eeedab --- /dev/null +++ b/src/scriptutils.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "scriptutils.h" + +#include "key.h" +#include "keystore.h" +#include "script/standard.h" + +#include <boost/foreach.hpp> + +using namespace std; + +typedef vector<unsigned char> valtype; + +unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore) +{ + unsigned int nResult = 0; + BOOST_FOREACH(const valtype& pubkey, pubkeys) + { + CKeyID keyID = CPubKey(pubkey).GetID(); + if (keystore.HaveKey(keyID)) + ++nResult; + } + return nResult; +} + +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +{ + CScript script; + script.SetDestination(dest); + return IsMine(keystore, script); +} + +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +{ + vector<valtype> vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + return ISMINE_NO; + } + + CKeyID keyID; + switch (whichType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + break; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_SCRIPTHASH: + { + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + CScript subscript; + if (keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript); + if (ret == ISMINE_SPENDABLE) + return ret; + } + break; + } + case TX_MULTISIG: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (HaveKeys(keys, keystore) == keys.size()) + return ISMINE_SPENDABLE; + break; + } + } + + if (keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + return ISMINE_NO; +} + +class CAffectedKeysVisitor : public boost::static_visitor<void> { +private: + const CKeyStore &keystore; + std::vector<CKeyID> &vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript &script) { + txnouttype type; + std::vector<CTxDestination> vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + BOOST_FOREACH(const CTxDestination &dest, vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID &keyId) { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID &scriptId) { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination &none) {} +}; + +void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys) { + CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey); +} diff --git a/src/scriptutils.h b/src/scriptutils.h new file mode 100644 index 0000000000..98080fc456 --- /dev/null +++ b/src/scriptutils.h @@ -0,0 +1,29 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPTUTILS +#define H_BITCOIN_SCRIPTUTILS + +#include "key.h" +#include "script/script.h" + +class CKeyStore; + +/** IsMine() return codes */ +enum isminetype +{ + ISMINE_NO = 0, + ISMINE_WATCH_ONLY = 1, + ISMINE_SPENDABLE = 2, + ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE +}; +/** used for bitflags of isminetype */ +typedef uint8_t isminefilter; + +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); +void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); + +#endif // H_BITCOIN_SCRIPT diff --git a/src/serialize.h b/src/serialize.h index 1440676a16..dba3460d1b 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -91,9 +91,9 @@ enum /* Implement three methods for serializable objects. These are actually wrappers over * "SerializationOp" template, which implements the body of each class' serialization - * code. Adding "IMPLEMENT_SERIALIZE" in the body of the class causes these wrappers to be + * code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be * added as members. */ -#define IMPLEMENT_SERIALIZE \ +#define ADD_SERIALIZE_METHODS \ size_t GetSerializeSize(int nType, int nVersion) const { \ CSizeComputer s(nType, nVersion); \ NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize(), nType, nVersion);\ @@ -807,7 +807,7 @@ void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion) // -// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// Support for ADD_SERIALIZE_METHODS and READWRITE macro // struct CSerActionSerialize { diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 8fa38c3605..fa4edff63f 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -12,7 +12,7 @@ #include "main.h" #include "net.h" #include "pow.h" -#include "script.h" +#include "script/sign.h" #include "serialize.h" #include "util.h" diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 0ac3e9a363..fe68e9e974 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -9,7 +9,7 @@ #include "data/base58_keys_valid.json.h" #include "key.h" -#include "script.h" +#include "script/script.h" #include "uint256.h" #include "util.h" diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp index a9798623ea..a17099de72 100644 --- a/src/test/canonical_tests.cpp +++ b/src/test/canonical_tests.cpp @@ -8,9 +8,11 @@ #include "data/sig_noncanonical.json.h" #include "data/sig_canonical.json.h" +#include "key.h" #include "random.h" -#include "script.h" +#include "script/interpreter.h" #include "util.h" +#include "utilstrencodings.h" #include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 864d90128c..203c20731a 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -5,7 +5,7 @@ #include "key.h" #include "base58.h" -#include "script.h" +#include "script/script.h" #include "uint256.h" #include "util.h" diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 02c6d095f2..6c5afa130c 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -5,7 +5,10 @@ #include "key.h" #include "keystore.h" #include "main.h" -#include "script.h" +#include "script/script.h" +#include "script/interpreter.h" +#include "script/sign.h" +#include "scriptutils.h" #include "uint256.h" #include <boost/assign/std/vector.hpp> diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 51ff1ffbc9..b7e7487bb2 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script.h" - #include "key.h" #include "keystore.h" #include "main.h" -#include "script.h" +#include "script/script.h" +#include "script/sign.h" +#include "scriptutils.h" #include <vector> diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 77c44501ab..88efc38964 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2,15 +2,14 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script.h" - #include "data/script_invalid.json.h" #include "data/script_valid.json.h" #include "key.h" #include "keystore.h" #include "main.h" -#include "script.h" +#include "script/script.h" +#include "script/sign.h" #include "core_io.h" #include <fstream> diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp index cd194cc4d9..ac60fa426f 100644 --- a/src/test/scriptnum_tests.cpp +++ b/src/test/scriptnum_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "bignum.h" -#include "script.h" +#include "script/script.h" #include <boost/test/unit_test.hpp> #include <limits.h> #include <stdint.h> diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index bff151cdda..8abde887ce 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -6,7 +6,8 @@ #include "main.h" #include "random.h" #include "serialize.h" -#include "script.h" +#include "script/script.h" +#include "script/interpreter.h" #include "util.h" #include "version.h" diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 722f14a989..2d10c356ac 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "key.h" -#include "script.h" +#include "script/script.h" #include "uint256.h" #include <vector> diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 03919e7c7d..943568e89d 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -8,7 +8,7 @@ #include "key.h" #include "keystore.h" #include "main.h" -#include "script.h" +#include "script/script.h" #include "core_io.h" #include <map> diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f059e69ac7..6bbadc8345 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -606,7 +606,7 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, deltas.first += dPriorityDelta; deltas.second += nFeeDelta; } - LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash.c_str(), dPriorityDelta, nFeeDelta); + LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, nFeeDelta); } void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta) diff --git a/src/uint256.h b/src/uint256.h index d1a822af0d..6bb9a59400 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -10,6 +10,7 @@ #include <stdexcept> #include <stdint.h> #include <string> +#include <cstring> #include <vector> class uint_error : public std::runtime_error { @@ -215,8 +216,8 @@ public: friend inline const base_uint operator>>(const base_uint& a, int shift) { return base_uint(a) >>= shift; } friend inline const base_uint operator<<(const base_uint& a, int shift) { return base_uint(a) <<= shift; } friend inline const base_uint operator*(const base_uint& a, uint32_t b) { return base_uint(a) *= b; } - friend inline bool operator==(const base_uint& a, const base_uint& b) { return a.CompareTo(b) == 0; } - friend inline bool operator!=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) != 0; } + friend inline bool operator==(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) == 0; } + friend inline bool operator!=(const base_uint& a, const base_uint& b) { return memcmp(a.pn, b.pn, sizeof(a.pn)) != 0; } friend inline bool operator>(const base_uint& a, const base_uint& b) { return a.CompareTo(b) > 0; } friend inline bool operator<(const base_uint& a, const base_uint& b) { return a.CompareTo(b) < 0; } friend inline bool operator>=(const base_uint& a, const base_uint& b) { return a.CompareTo(b) >= 0; } diff --git a/src/univalue/univalue_write.cpp b/src/univalue/univalue_write.cpp index 042091a827..9565cfa11a 100644 --- a/src/univalue/univalue_write.cpp +++ b/src/univalue/univalue_write.cpp @@ -70,15 +70,9 @@ string UniValue::write(unsigned int prettyIndent, return s; } -static string spaceStr; - -static string indentStr(unsigned int prettyIndent, unsigned int indentLevel) +static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s) { - unsigned int spaces = prettyIndent * indentLevel; - while (spaceStr.size() < spaces) - spaceStr += " "; - - return spaceStr.substr(0, spaces); + s.append(prettyIndent * indentLevel, ' '); } void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const @@ -89,7 +83,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s for (unsigned int i = 0; i < values.size(); i++) { if (prettyIndent) - s += indentStr(prettyIndent, indentLevel); + indentStr(prettyIndent, indentLevel, s); s += values[i].write(prettyIndent, indentLevel + 1); if (i != (values.size() - 1)) { s += ","; @@ -101,7 +95,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s } if (prettyIndent) - s += indentStr(prettyIndent, indentLevel - 1); + indentStr(prettyIndent, indentLevel - 1, s); s += "]"; } @@ -113,7 +107,7 @@ void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, for (unsigned int i = 0; i < keys.size(); i++) { if (prettyIndent) - s += indentStr(prettyIndent, indentLevel); + indentStr(prettyIndent, indentLevel, s); s += "\"" + json_escape(keys[i]) + "\":"; if (prettyIndent) s += " "; @@ -125,7 +119,7 @@ void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, } if (prettyIndent) - s += indentStr(prettyIndent, indentLevel - 1); + indentStr(prettyIndent, indentLevel - 1, s); s += "}"; } diff --git a/src/wallet.cpp b/src/wallet.cpp index 18a5b3971c..218a137966 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -9,6 +9,8 @@ #include "checkpoints.h" #include "coincontrol.h" #include "net.h" +#include "script/script.h" +#include "script/sign.h" #include "timedata.h" #include "util.h" #include "utilmoneystr.h" @@ -42,7 +44,7 @@ struct CompareValueOnly std::string COutput::ToString() const { - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue)); } const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const @@ -2113,7 +2115,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { // iterate over all wallet transactions... const CWalletTx &wtx = (*it).second; - std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); + BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block int nHeight = blit->second->nHeight; @@ -2233,7 +2235,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) } // Is the tx in a block that's in the main chain - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; @@ -2250,7 +2252,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const AssertLockHeld(cs_main); // Find the block it claims to be in - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; diff --git a/src/wallet.h b/src/wallet.h index cea61afed3..6788986f88 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -11,6 +11,7 @@ #include "key.h" #include "keystore.h" #include "main.h" +#include "scriptutils.h" #include "ui_interface.h" #include "walletdb.h" @@ -64,7 +65,7 @@ public: CKeyPool(); CKeyPool(const CPubKey& vchPubKeyIn); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -492,7 +493,7 @@ public: fMerkleVerified = false; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -605,25 +606,22 @@ public: nOrderPos = -1; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - - CWalletTx* pthis = const_cast<CWalletTx*>(this); - if (fRead) - pthis->Init(NULL); + if (ser_action.ForRead()) + Init(NULL); char fSpent = false; - if (!fRead) + if (!ser_action.ForRead()) { - pthis->mapValue["fromaccount"] = pthis->strFromAccount; + mapValue["fromaccount"] = strFromAccount; - WriteOrderPos(pthis->nOrderPos, pthis->mapValue); + WriteOrderPos(nOrderPos, mapValue); if (nTimeSmart) - pthis->mapValue["timesmart"] = strprintf("%u", nTimeSmart); + mapValue["timesmart"] = strprintf("%u", nTimeSmart); } READWRITE(*(CMerkleTx*)this); @@ -636,13 +634,13 @@ public: READWRITE(fFromMe); READWRITE(fSpent); - if (fRead) + if (ser_action.ForRead()) { - pthis->strFromAccount = pthis->mapValue["fromaccount"]; + strFromAccount = mapValue["fromaccount"]; - ReadOrderPos(pthis->nOrderPos, pthis->mapValue); + ReadOrderPos(nOrderPos, mapValue); - pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0; + nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; } mapValue.erase("fromaccount"); @@ -896,7 +894,7 @@ public: CWalletKey(int64_t nExpires=0); - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -932,7 +930,7 @@ public: vchPubKey = CPubKey(); } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { @@ -975,13 +973,10 @@ public: nEntryNo = 0; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - bool fRead = ser_action.ForRead(); - - CAccountingEntry& me = *const_cast<CAccountingEntry*>(this); if (!(nType & SER_GETHASH)) READWRITE(nVersion); // Note: strAccount is serialized as part of the key, not here. @@ -989,9 +984,9 @@ public: READWRITE(nTime); READWRITE(LIMITED_STRING(strOtherAccount, 65536)); - if (!fRead) + if (!ser_action.ForRead()) { - WriteOrderPos(nOrderPos, me.mapValue); + WriteOrderPos(nOrderPos, mapValue); if (!(mapValue.empty() && _ssExtra.empty())) { @@ -999,26 +994,26 @@ public: ss.insert(ss.begin(), '\0'); ss << mapValue; ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); - me.strComment.append(ss.str()); + strComment.append(ss.str()); } } READWRITE(LIMITED_STRING(strComment, 65536)); size_t nSepPos = strComment.find("\0", 0, 1); - if (fRead) + if (ser_action.ForRead()) { - me.mapValue.clear(); + mapValue.clear(); if (std::string::npos != nSepPos) { CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); - ss >> me.mapValue; - me._ssExtra = std::vector<char>(ss.begin(), ss.end()); + ss >> mapValue; + _ssExtra = std::vector<char>(ss.begin(), ss.end()); } - ReadOrderPos(me.nOrderPos, me.mapValue); + ReadOrderPos(nOrderPos, mapValue); } if (std::string::npos != nSepPos) - me.strComment.erase(nSepPos); + strComment.erase(nSepPos); mapValue.erase("n"); } diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 2fa6071658..48045b98c8 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -281,8 +281,12 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) nOrderPos = nOrderPosNext++; nOrderPosOffsets.push_back(nOrderPos); - if (pacentry) - // Have to write accounting regardless, since we don't keep it in memory + if (pwtx) + { + if (!WriteTx(pwtx->GetHash(), *pwtx)) + return DB_LOAD_FAIL; + } + else if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DB_LOAD_FAIL; } @@ -311,6 +315,7 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) return DB_LOAD_FAIL; } } + WriteOrderPosNext(nOrderPosNext); return DB_LOAD_OK; } diff --git a/src/walletdb.h b/src/walletdb.h index cf1a66216e..ce63bb0b97 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -55,7 +55,7 @@ public: nCreateTime = nCreateTime_; } - IMPLEMENT_SERIALIZE; + ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { |