From 336fe971e68f0336d42e1fa930b6a9c717f612e0 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Sun, 6 Jan 2013 04:30:00 -0800 Subject: Get rid of db dependencies on main --- src/db.cpp | 11 +++++++---- src/db.h | 10 +++++++--- src/init.cpp | 1 + src/wallet.h | 4 +++- src/walletdb.h | 4 ++++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/db.cpp b/src/db.cpp index fd4c67d552..1f53917602 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -5,10 +5,12 @@ #include "db.h" #include "util.h" -#include "main.h" +#include "hash.h" +#include "addrman.h" #include #include #include +#include #ifndef WIN32 #include "sys/stat.h" @@ -486,6 +488,7 @@ void CDBEnv::Flush(bool fShutdown) // CAddrDB // +unsigned char CAddrDB::pchMessageStart[4] = { 0x00, 0x00, 0x00, 0x00 }; CAddrDB::CAddrDB() { @@ -501,7 +504,7 @@ bool CAddrDB::Write(const CAddrMan& addr) // serialize addresses, checksum data up to that point, then append csum CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(pchMessageStart); + ssPeers << FLATDATA(CAddrDB::pchMessageStart); ssPeers << addr; uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); ssPeers << hash; @@ -566,11 +569,11 @@ bool CAddrDB::Read(CAddrMan& addr) unsigned char pchMsgTmp[4]; try { - // de-serialize file header (pchMessageStart magic number) and + // de-serialize file header (CAddrDB::pchMessageStart magic number) and ssPeers >> FLATDATA(pchMsgTmp); // verify the network matches ours - if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp))) + if (memcmp(pchMsgTmp, CAddrDB::pchMessageStart, sizeof(pchMsgTmp))) return error("CAddrman::Read() : invalid network magic number"); // de-serialize address data into one CAddrMan object diff --git a/src/db.h b/src/db.h index ea440c4960..92241f6cf2 100644 --- a/src/db.h +++ b/src/db.h @@ -5,22 +5,22 @@ #ifndef BITCOIN_DB_H #define BITCOIN_DB_H -#include "main.h" +#include "sync.h" +#include "serialize.h" #include #include #include +#include #include -class CAddress; class CAddrMan; class CBlockLocator; class CDiskBlockIndex; class CMasterKey; class COutPoint; class CWallet; -class CWalletTx; extern unsigned int nWalletDBUpdated; @@ -318,10 +318,14 @@ class CAddrDB { private: boost::filesystem::path pathAddr; + static unsigned char pchMessageStart[4]; + public: CAddrDB(); bool Write(const CAddrMan& addr); bool Read(CAddrMan& addr); + + static void SetMessageStart(unsigned char _pchMessageStart[]) { memcpy(CAddrDB::pchMessageStart, _pchMessageStart, sizeof(CAddrDB::pchMessageStart)); } }; #endif // BITCOIN_DB_H diff --git a/src/init.cpp b/src/init.cpp index dcc43d8368..e4cb2f1d00 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -928,6 +928,7 @@ bool AppInit2(boost::thread_group& threadGroup) nStart = GetTimeMillis(); { + CAddrDB::SetMessageStart(pchMessageStart); CAddrDB adb; if (!adb.Read(addrman)) printf("Invalid or missing peers.dat; recreating\n"); diff --git a/src/wallet.h b/src/wallet.h index 7fcb8e13ce..22dce78e91 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_WALLET_H #define BITCOIN_WALLET_H +#include "walletdb.h" + #include #include @@ -16,12 +18,12 @@ #include "script.h" #include "ui_interface.h" #include "util.h" -#include "walletdb.h" class CAccountingEntry; class CWalletTx; class CReserveKey; class COutput; +class CWalletDB; /** (client) version numbers for particular wallet features */ enum WalletFeature diff --git a/src/walletdb.h b/src/walletdb.h index 8ae6c3ff49..9732eb29e4 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -11,6 +11,8 @@ class CKeyPool; class CAccount; class CAccountingEntry; +class CWallet; +class CWalletTx; /** Error statuses for the wallet database */ enum DBErrors @@ -160,4 +162,6 @@ public: static bool Recover(CDBEnv& dbenv, std::string filename); }; +bool BackupWallet(const CWallet& wallet, const std::string& strDest); + #endif // BITCOIN_WALLETDB_H -- cgit v1.2.3 From 8926263dde10bed3f42e21e5f106365f1192e6c0 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 7 Jan 2013 06:39:53 -0800 Subject: Moved PushGetBlocks to main.cpp to eliminate dependence of net.cpp on CBlockLocator. --- src/main.cpp | 19 +++++++++++++++---- src/main.h | 3 +++ src/net.cpp | 11 ----------- src/net.h | 1 - 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b8b7771611..65ae78a136 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2208,6 +2208,17 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) +{ + // Filter out duplicate requests + if (pindexBegin == pnode->pindexLastGetBlocksBegin && hashEnd == pnode->hashLastGetBlocksEnd) + return; + pnode->pindexLastGetBlocksBegin = pindexBegin; + pnode->hashLastGetBlocksEnd = hashEnd; + + pnode->PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); +} + bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { // Check for duplicate @@ -2253,7 +2264,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); // Ask this guy to fill in what we're missing - pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); + PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(pblock2)); } return true; } @@ -3357,12 +3368,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!fImporting && !fReindex) pfrom->AskFor(inv); } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { - pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have // the last block in an inv bundle sent in response to getblocks. Try to detect // this situation and push another getblocks to continue. - pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); + PushGetBlocks(pfrom, mapBlockIndex[inv.hash], uint256(0)); if (fDebug) printf("force request: %s\n", inv.ToString().c_str()); } @@ -3839,7 +3850,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; - pto->PushGetBlocks(pindexBest, uint256(0)); + PushGetBlocks(pto, pindexBest, uint256(0)); } // Resend wallet transactions that haven't gotten in a block yet diff --git a/src/main.h b/src/main.h index fae37ea512..feaa10318c 100644 --- a/src/main.h +++ b/src/main.h @@ -122,6 +122,9 @@ void RegisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn); /** Push an updated transaction to all registered wallets */ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); + +void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd); + /** Process an incoming block */ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); /** Check whether enough disk space is available for an incoming block */ diff --git a/src/net.cpp b/src/net.cpp index 54ed1d9b51..8929919f03 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -79,17 +79,6 @@ unsigned short GetListenPort() return (unsigned short)(GetArg("-port", GetDefaultPort())); } -void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) -{ - // Filter out duplicate requests - if (pindexBegin == pindexLastGetBlocksBegin && hashEnd == hashLastGetBlocksEnd) - return; - pindexLastGetBlocksBegin = pindexBegin; - hashLastGetBlocksEnd = hashEnd; - - PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); -} - // find 'best' local address for a particular peer bool GetLocal(CService& addr, const CNetAddr *paddrPeer) { diff --git a/src/net.h b/src/net.h index af66eed070..6f7bea9394 100644 --- a/src/net.h +++ b/src/net.h @@ -600,7 +600,6 @@ public: } } - void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); bool IsSubscribed(unsigned int nChannel); void Subscribe(unsigned int nChannel, unsigned int nHops=0); void CancelSubscribe(unsigned int nChannel); -- cgit v1.2.3 From 4751d07efdc3924a005bbe61b358d320f0c710af Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 7 Jan 2013 07:35:31 -0800 Subject: Moved unrelated-to-network calls in StartNode and StopNode into init.cpp --- src/init.cpp | 2 ++ src/net.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index e4cb2f1d00..48fd5ae97f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -45,6 +45,7 @@ enum BindFlags { BF_REPORT_ERROR = (1U << 1) }; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -99,6 +100,7 @@ void Shutdown() StopRPCThreads(); ShutdownRPCMining(); bitdb.Flush(false); + GenerateBitcoins(false, NULL); StopNode(); { LOCK(cs_main); diff --git a/src/net.cpp b/src/net.cpp index 8929919f03..7a1fcc5033 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1850,7 +1850,6 @@ void StartNode(boost::thread_group& threadGroup) bool StopNode() { printf("StopNode()\n"); - GenerateBitcoins(false, NULL); MapPort(false); nTransactionsUpdated++; if (semOutbound) -- cgit v1.2.3 From 663224c2324d64134f8587fe77d1d787c0353b20 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 7 Jan 2013 08:07:51 -0800 Subject: Removed net.cpp's dependency on init.h. Added explicit include of main.h in init.cpp, changed include of init.h to include of main.h in net.cpp. Added function registration for net.cpp in init.cpp's network initialization. Removed protocol.cpp's dependency on main.h. TODO: Remove main.h include in net.cpp. --- src/init.cpp | 7 ++++++- src/net.cpp | 28 +++++++++++++++++++++++++--- src/net.h | 11 +++++++++++ src/protocol.cpp | 1 - 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 48fd5ae97f..1eee4d2470 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -3,11 +3,12 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "init.h" +#include "main.h" #include "txdb.h" #include "walletdb.h" #include "bitcoinrpc.h" #include "net.h" -#include "init.h" #include "util.h" #include "ui_interface.h" #include "checkpoints.h" @@ -569,6 +570,10 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 6: network initialization + SetProcessMessagesHandler(ProcessMessages); + SetSendMessagesHandler(SendMessages); + SetStartShutdownHandler(StartShutdown); + int nSocksVersion = GetArg("-socks", 5); if (nSocksVersion != 4 && nSocksVersion != 5) return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion)); diff --git a/src/net.cpp b/src/net.cpp index 7a1fcc5033..5932f5f8e3 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -5,7 +5,7 @@ #include "db.h" #include "net.h" -#include "init.h" +#include "main.h" #include "addrman.h" #include "ui_interface.h" #include "script.h" @@ -68,6 +68,28 @@ CCriticalSection cs_vAddedNodes; static CSemaphore *semOutbound = NULL; +// +// Handlers that need to be registered +// +static ProcessMessagesHandler fnProcessMessages = NULL; +static SendMessagesHandler fnSendMessages = NULL; +static StartShutdownHandler fnStartShutdown = NULL; + +void SetProcessMessagesHandler(ProcessMessagesHandler handler) +{ + fnProcessMessages = handler; +} + +void SetSendMessagesHandler(SendMessagesHandler handler) +{ + fnSendMessages = handler; +} + +void SetStartShutdownHandler(StartShutdownHandler handler) +{ + fnStartShutdown = handler; +} + void AddOneShot(string strDest) { LOCK(cs_vOneShots); @@ -1632,8 +1654,8 @@ void ThreadMessageHandler() // Send messages { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - SendMessages(pnode, pnode == pnodeTrickle); + if (lockSend && fnSendMessages) + fnSendMessages(pnode, pnode == pnodeTrickle); } boost::this_thread::interruption_point(); } diff --git a/src/net.h b/src/net.h index 6f7bea9394..1c8c99aa3b 100644 --- a/src/net.h +++ b/src/net.h @@ -45,6 +45,17 @@ void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); +// +// Handlers that require registration +// +typedef bool (*ProcessMessagesHandler)(CNode* pfrom); +typedef bool (*SendMessagesHandler)(CNode* pto, bool fSendTrickle); +typedef void (*StartShutdownHandler)(); + +void SetProcessMessagesHandler(ProcessMessagesHandler handler); +void SetSendMessagesHandler(SendMessagesHandler handler); +void SetStartShutdownHandler(StartShutdownHandler handler); + enum { LOCAL_NONE, // unknown diff --git a/src/protocol.cpp b/src/protocol.cpp index 88bbe49afd..1e22467a33 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -6,7 +6,6 @@ #include "protocol.h" #include "util.h" #include "netbase.h" -#include "main.h" #ifndef WIN32 # include -- cgit v1.2.3 From effc2770f50554416e7d7b27f5c5b5cef9489440 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 03:02:51 -0800 Subject: Created core.h/core.cpp, added to makefiles. Started moving core structures from main to core beginning with COutPoint. --- bitcoin-qt.pro | 2 ++ src/core.cpp | 6 ++++++ src/core.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.h | 42 +------------------------------------- src/makefile.linux-mingw | 1 + src/makefile.mingw | 1 + src/makefile.osx | 1 + src/makefile.unix | 1 + 8 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 src/core.cpp create mode 100644 src/core.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index ec9f5e4a1a..526965d867 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -154,6 +154,7 @@ HEADERS += src/qt/bitcoingui.h \ src/hash.h \ src/uint256.h \ src/serialize.h \ + src/core.h \ src/main.h \ src/net.h \ src/key.h \ @@ -234,6 +235,7 @@ SOURCES += src/qt/bitcoin.cpp \ src/netbase.cpp \ src/key.cpp \ src/script.cpp \ + src/core.cpp \ src/main.cpp \ src/init.cpp \ src/net.cpp \ diff --git a/src/core.cpp b/src/core.cpp new file mode 100644 index 0000000000..cb1053b69d --- /dev/null +++ b/src/core.cpp @@ -0,0 +1,6 @@ +// 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 "core.h" \ No newline at end of file diff --git a/src/core.h b/src/core.h new file mode 100644 index 0000000000..77bd8472ed --- /dev/null +++ b/src/core.h @@ -0,0 +1,53 @@ +// 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 BITCOIN_CORE_H +#define BITCOIN_CORE_H + +#include "uint256.h" +#include "serialize.h" +#include "util.h" + +#include + +/** An outpoint - a combination of a transaction hash and an index n into its vout */ +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = (unsigned int) -1; } + bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + std::string ToString() const + { + return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + +#endif \ No newline at end of file diff --git a/src/main.h b/src/main.h index feaa10318c..099e436d9e 100644 --- a/src/main.h +++ b/src/main.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_MAIN_H #define BITCOIN_MAIN_H +#include "core.h" #include "bignum.h" #include "sync.h" #include "net.h" @@ -267,47 +268,6 @@ public: -/** An outpoint - a combination of a transaction hash and an index n into its vout */ -class COutPoint -{ -public: - uint256 hash; - unsigned int n; - - COutPoint() { SetNull(); } - COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } - IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) - void SetNull() { hash = 0; n = (unsigned int) -1; } - bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); } - - friend bool operator<(const COutPoint& a, const COutPoint& b) - { - return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); - } - - friend bool operator==(const COutPoint& a, const COutPoint& b) - { - return (a.hash == b.hash && a.n == b.n); - } - - friend bool operator!=(const COutPoint& a, const COutPoint& b) - { - return !(a == b); - } - - std::string ToString() const - { - return strprintf("COutPoint(%s, %u)", hash.ToString().c_str(), n); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - /** An input of a transaction. It contains the location of the previous * transaction's output that it claims and a signature that matches the diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 114a9491bd..9cfab5942a 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -73,6 +73,7 @@ OBJS= \ obj/init.o \ obj/bitcoind.o \ obj/keystore.o \ + obj/core.o \ obj/main.o \ obj/net.o \ obj/protocol.o \ diff --git a/src/makefile.mingw b/src/makefile.mingw index 34ddc3eece..33cc7e6b4a 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -81,6 +81,7 @@ OBJS= \ obj/init.o \ obj/bitcoind.o \ obj/keystore.o \ + obj/core.o \ obj/main.o \ obj/net.o \ obj/protocol.o \ diff --git a/src/makefile.osx b/src/makefile.osx index 4ee0edcb95..bef0ef3518 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -84,6 +84,7 @@ OBJS= \ obj/init.o \ obj/bitcoind.o \ obj/keystore.o \ + obj/core.o \ obj/main.o \ obj/net.o \ obj/protocol.o \ diff --git a/src/makefile.unix b/src/makefile.unix index f8042b2930..a83bab1047 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -123,6 +123,7 @@ OBJS= \ obj/init.o \ obj/bitcoind.o \ obj/keystore.o \ + obj/core.o \ obj/main.o \ obj/net.o \ obj/protocol.o \ -- cgit v1.2.3 From 788536f1755c6a9ea81394a2199ca27c9e90944e Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 03:42:22 -0800 Subject: Moved CInPoint to core. Removed GetMinFee from CTransaction and made it a regular function in main. --- src/core.h | 15 +++++++++++++++ src/main.cpp | 11 +++++------ src/main.h | 18 ++---------------- src/wallet.cpp | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/core.h b/src/core.h index 77bd8472ed..bd1ddbf5a4 100644 --- a/src/core.h +++ b/src/core.h @@ -11,6 +11,8 @@ #include +class CTransaction; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -50,4 +52,17 @@ public: } }; +/** An inpoint - a combination of a transaction and an index n into its vin */ +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = (unsigned int) -1; } + bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } +}; + #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 65ae78a136..5a1345d414 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -591,13 +591,12 @@ bool CTransaction::CheckTransaction(CValidationState &state) const return true; } -int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, - enum GetMinFee_mode mode) const +int64 GetMinFee(const CTransaction& tx, unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode) { // Base fee is either nMinTxFee or nMinRelayTxFee - int64 nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee; + int64 nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee; - unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + unsigned int nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; @@ -621,7 +620,7 @@ int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, // To limit dust spam, require base fee if any output is less than 0.01 if (nMinFee < nBaseFee) { - BOOST_FOREACH(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, tx.vout) if (txout.nValue < CENT) nMinFee = nBaseFee; } @@ -757,7 +756,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block - int64 txMinFee = tx.GetMinFee(1000, true, GMF_RELAY); + int64 txMinFee = GetMinFee(tx, 1000, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, hash.ToString().c_str(), diff --git a/src/main.h b/src/main.h index 099e436d9e..c3c7ee3966 100644 --- a/src/main.h +++ b/src/main.h @@ -253,20 +253,6 @@ struct CDiskTxPos : public CDiskBlockPos }; -/** An inpoint - a combination of a transaction and an index n into its vin */ -class CInPoint -{ -public: - CTransaction* ptx; - unsigned int n; - - CInPoint() { SetNull(); } - CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = (unsigned int) -1; } - bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } -}; - - /** An input of a transaction. It contains the location of the previous @@ -424,6 +410,8 @@ enum GetMinFee_mode GMF_SEND, }; +int64 GetMinFee(const CTransaction& tx, unsigned int nBlockSize = 1, bool fAllowFree = true, enum GetMinFee_mode mode = GMF_BLOCK); + /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ @@ -575,8 +563,6 @@ public: return dPriority > COIN * 144 / 250; } - int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const; - friend bool operator==(const CTransaction& a, const CTransaction& b) { return (a.nVersion == b.nVersion && diff --git a/src/wallet.cpp b/src/wallet.cpp index 549d9bbf4b..da75752bb4 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1277,7 +1277,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); bool fAllowFree = CTransaction::AllowFree(dPriority); - int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND); + int64 nMinFee = GetMinFee(wtxNew, 1, fAllowFree, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { nFeeRet = max(nPayFee, nMinFee); -- cgit v1.2.3 From 05df3fc68d68e87415ed9e534db3ea3160dc3092 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 04:17:15 -0800 Subject: Removed AcceptToMemoryPool method from CTransaction. This method belongs to the mempool instance. Removed AreInputsStandard from CTransaction, made it a regular function in main. Moved CTransaction::GetOutputFor to CCoinsViewCache. Moved GetLegacySigOpCount and GetP2SHSigOpCount out of CTransaction into regular functions in main. Moved GetValueIn and HaveInputs from CTransaction into CCoinsViewCache. Moved AllowFree, ClientCheckInputs, CheckInputs, UpdateCoins, and CheckTransaction out of CTransaction and into main. Moved IsStandard and IsFinal out of CTransaction and put them in main as IsStandardTx and IsFinalTx. Moved GetValueOut out of CTransaction into main. Moved CTxIn, CTxOut, and CTransaction into core. Added minimum fee parameter to CTxOut::IsDust() temporarily until CTransaction is moved to core.h so that CTxOut needn't know about CTransaction. --- src/core.cpp | 3 +- src/core.h | 277 +++++++++++++++++++++++++++- src/init.cpp | 1 + src/main.cpp | 276 ++++++++++++++-------------- src/main.h | 406 +++++++---------------------------------- src/qt/transactiondesc.cpp | 4 +- src/qt/transactionrecord.cpp | 4 +- src/rpcrawtransaction.cpp | 2 +- src/rpcwallet.cpp | 10 +- src/script.h | 2 +- src/test/data/tx_invalid.json | 2 +- src/test/data/tx_valid.json | 2 +- src/test/script_P2SH_tests.cpp | 18 +- src/test/transaction_tests.cpp | 24 +-- src/wallet.cpp | 14 +- src/wallet.h | 4 +- src/walletdb.cpp | 2 +- 17 files changed, 527 insertions(+), 524 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index cb1053b69d..b12c90efe8 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -3,4 +3,5 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "core.h" \ No newline at end of file +#include "core.h" + diff --git a/src/core.h b/src/core.h index bd1ddbf5a4..21ca6618a2 100644 --- a/src/core.h +++ b/src/core.h @@ -8,6 +8,7 @@ #include "uint256.h" #include "serialize.h" #include "util.h" +#include "script.h" #include @@ -65,4 +66,278 @@ public: bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } }; -#endif \ No newline at end of file +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = std::numeric_limits::max(); + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == std::numeric_limits::max()); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToString() const + { + std::string str; + str += "CTxIn("; + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); + if (nSequence != std::numeric_limits::max()) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** An output of a transaction. It contains the public key that the next input + * must be able to sign with to claim it. + */ +class CTxOut +{ +public: + int64 nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() const + { + return (nValue == -1); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsDust(int64 nMinRelayTxFee) const + { + // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, + // which has units satoshis-per-kilobyte. + // If you'd pay more than 1/3 in fees + // to spend something, then we consider it dust. + // A typical txout is 33 bytes big, and will + // need a CTxIn of at least 148 bytes to spend, + // so dust is a txout less than 54 uBTC + // (5430 satoshis) with default nMinRelayTxFee + return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < nMinRelayTxFee); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +public: + static int64 nMinTxFee; + static int64 nMinRelayTxFee; + static const int CURRENT_VERSION=1; + int nVersion; + std::vector vin; + std::vector vout; + unsigned int nLockTime; + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = CTransaction::CURRENT_VERSION; + vin.clear(); + vout.clear(); + nLockTime = 0; + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (unsigned int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = std::numeric_limits::max(); + for (unsigned int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + std::string ToString() const + { + std::string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", + GetHash().ToString().substr(0,10).c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + +#endif diff --git a/src/init.cpp b/src/init.cpp index 1eee4d2470..a1dbfd2a5f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -5,6 +5,7 @@ #include "init.h" #include "main.h" +#include "core.h" #include "txdb.h" #include "walletdb.h" #include "bitcoinrpc.h" diff --git a/src/main.cpp b/src/main.cpp index 5a1345d414..28b32101d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -357,41 +357,23 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -////////////////////////////////////////////////////////////////////////////// -// -// CTransaction / CTxOut -// - -bool CTxOut::IsDust() const +bool IsStandardTx(const CTransaction& tx) { - // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, - // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees - // to spend something, then we consider it dust. - // A typical txout is 33 bytes big, and will - // need a CTxIn of at least 148 bytes to spend, - // so dust is a txout less than 54 uBTC - // (5430 satoshis) with default nMinRelayTxFee - return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < CTransaction::nMinRelayTxFee); -} - -bool CTransaction::IsStandard() const -{ - if (nVersion > CTransaction::CURRENT_VERSION) + if (tx.nVersion > CTransaction::CURRENT_VERSION) return false; - if (!IsFinal()) + if (!IsFinalTx(tx)) return false; // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); if (sz >= MAX_STANDARD_TX_SIZE) return false; - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 @@ -401,15 +383,47 @@ bool CTransaction::IsStandard() const if (!txin.scriptSig.IsPushOnly()) return false; } - BOOST_FOREACH(const CTxOut& txout, vout) { + BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (!::IsStandard(txout.scriptPubKey)) return false; - if (txout.IsDust()) + if (txout.IsDust(CTransaction::nMinRelayTxFee)) return false; } return true; } +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) +{ + // Time based nLockTime implemented in 0.1.6 + if (tx.nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64)tx.nLockTime < ((int64)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (!txin.IsFinal()) + return false; + return true; +} + +/** Amount of bitcoins spent by the transaction. + @return sum of all outputs (note: does not include fees) + */ +int64 GetValueOut(const CTransaction& tx) +{ + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("GetValueOut() : value out of range"); + } + return nValueOut; +} + // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts @@ -421,14 +435,14 @@ bool CTransaction::IsStandard() const // expensive-to-check-upon-redemption script like: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // -bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const +bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs) { - if (IsCoinBase()) + if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally - for (unsigned int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut& prev = GetOutputFor(vin[i], mapInputs); + const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); vector > vSolutions; txnouttype whichType; @@ -446,7 +460,7 @@ bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const // beside "push data" in the scriptSig the // IsStandard() call returns false vector > stack; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0)) + if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, false, 0)) return false; if (whichType == TX_SCRIPTHASH) @@ -475,20 +489,34 @@ bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const return true; } -unsigned int CTransaction::GetLegacySigOpCount() const +unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } - BOOST_FOREACH(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, tx.vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } return nSigOps; } +unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& inputs) +{ + if (tx.IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); + } + return nSigOps; +} int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { @@ -543,25 +571,25 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -bool CTransaction::CheckTransaction(CValidationState &state) const +bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - if (vin.empty()) - return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); - if (vout.empty()) - return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + if (tx.vin.empty()) + return state.DoS(10, error("CheckTransaction() : vin empty")); + if (tx.vout.empty()) + return state.DoS(10, error("CheckTransaction() : vout empty")); // Size limits - if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); @@ -569,23 +597,23 @@ bool CTransaction::CheckTransaction(CValidationState &state) const // Check for duplicate inputs set vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); vInOutPoints.insert(txin.prevout); } - if (IsCoinBase()) + if (tx.IsCoinBase()) { - if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) + return state.DoS(100, error("CheckTransaction() : coinbase script size")); } else { - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + return state.DoS(10, error("CheckTransaction() : prevout is null")); } return true; @@ -657,7 +685,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn if (pfMissingInputs) *pfMissingInputs = false; - if (!tx.CheckTransaction(state)) + if (!CheckTransaction(tx, state)) return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction @@ -669,7 +697,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !tx.IsStandard()) + if (!fTestNet && !IsStandardTx(tx)) return error("CTxMemPool::accept() : nonstandard transaction type"); // is it already in the memory pool? @@ -694,7 +722,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn if (i != 0) return false; ptxOld = mapNextTx[outpoint].ptx; - if (ptxOld->IsFinal()) + if (IsFinalTx(*ptxOld)) return false; if (!tx.IsNewerThan(*ptxOld)) return false; @@ -734,7 +762,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn } // are the actual inputs available? - if (!tx.HaveInputs(view)) + if (!view.HaveInputs(tx)) return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); // Bring the best block into scope @@ -745,14 +773,14 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn } // Check for non-standard pay-to-script-hash in inputs - if (!tx.AreInputsStandard(view) && !fTestNet) + if (!AreInputsStandard(tx, view) && !fTestNet) return error("CTxMemPool::accept() : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64 nFees = tx.GetValueIn(view)-tx.GetValueOut(); + int64 nFees = view.GetValueIn(tx)-GetValueOut(tx); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block @@ -787,7 +815,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); } @@ -816,14 +844,6 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return true; } -bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) -{ - try { - return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); - } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); - } -} bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) { @@ -936,7 +956,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) { CValidationState state; - return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); + return mempool.accept(state, *this, fCheckInputs, fLimitFree, NULL); } @@ -1299,45 +1319,30 @@ void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) -const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view) +const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) { - const CCoins &coins = view.GetCoins(input.prevout.hash); + const CCoins &coins = GetCoins(input.prevout.hash); assert(coins.IsAvailable(input.prevout.n)); return coins.vout[input.prevout.n]; } -int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const +int64 CCoinsViewCache::GetValueIn(const CTransaction& tx) { - if (IsCoinBase()) + if (tx.IsCoinBase()) return 0; int64 nResult = 0; - for (unsigned int i = 0; i < vin.size(); i++) - nResult += GetOutputFor(vin[i], inputs).nValue; + for (unsigned int i = 0; i < tx.vin.size(); i++) + nResult += GetOutputFor(tx.vin[i]).nValue; return nResult; } -unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const -{ - if (IsCoinBase()) - return 0; - - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - const CTxOut &prevout = GetOutputFor(vin[i], inputs); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); - } - return nSigOps; -} - -void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) { // mark inputs spent - if (!IsCoinBase()) { - BOOST_FOREACH(const CTxIn &txin, vin) { + if (!tx.IsCoinBase()) { + BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; assert(coins.Spend(txin.prevout, undo)); @@ -1346,23 +1351,23 @@ void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, } // add outputs - assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); + assert(inputs.SetCoins(txhash, CCoins(tx, nHeight))); } -bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const +bool CCoinsViewCache::HaveInputs(const CTransaction& tx) { - if (!IsCoinBase()) { + if (!tx.IsCoinBase()) { // first check whether information about the prevout hash is available - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - if (!inputs.HaveCoins(prevout.hash)) + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + if (!HaveCoins(prevout.hash)) return false; } // then check whether the actual outputs are available - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - const CCoins &coins = inputs.GetCoins(prevout.hash); + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins &coins = GetCoins(prevout.hash); if (!coins.IsAvailable(prevout.n)) return false; } @@ -1382,26 +1387,26 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } -bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) const +bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) { - if (!IsCoinBase()) + if (!tx.IsCoinBase()) { if (pvChecks) - pvChecks->reserve(vin.size()); + pvChecks->reserve(tx.vin.size()); // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. - if (!HaveInputs(inputs)) - return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().c_str())); + if (!inputs.HaveInputs(tx)) + return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString().c_str())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; int64 nValueIn = 0; int64 nFees = 0; - for (unsigned int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; + const COutPoint &prevout = tx.vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); // If prev is coinbase, check that it's matured @@ -1417,13 +1422,13 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, } - if (nValueIn < GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().c_str())); + if (nValueIn < GetValueOut(tx)) + return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str())); // Tally transaction fees - int64 nTxFee = nValueIn - GetValueOut(); + int64 nTxFee = nValueIn - GetValueOut(tx); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs() : nFees out of range")); @@ -1436,12 +1441,12 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (fScriptChecks) { - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); // Verify signature - CScriptCheck check(coins, *this, i, flags, 0); + CScriptCheck check(coins, tx, i, flags, 0); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1449,7 +1454,7 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, if (flags & SCRIPT_VERIFY_STRICTENC) { // For now, check whether the failure was caused by non-canonical // encodings or not; if so, don't trigger DoS protection. - CScriptCheck check(coins, *this, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); + CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); if (check()) return state.Invalid(); } @@ -1464,7 +1469,6 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, - bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { assert(pindex == view.GetBestBlock()); @@ -1644,13 +1648,13 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi const CTransaction &tx = vtx[i]; nInputs += tx.vin.size(); - nSigOps += tx.GetLegacySigOpCount(); + nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock() : too many sigops")); if (!tx.IsCoinBase()) { - if (!tx.HaveInputs(view)) + if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); if (fStrictPayToScriptHash) @@ -1658,21 +1662,21 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. - nSigOps += tx.GetP2SHSigOpCount(view); + nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock() : too many sigops")); } - nFees += tx.GetValueIn(view)-tx.GetValueOut(); + nFees += view.GetValueIn(tx)-GetValueOut(tx); std::vector vChecks; - if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } CTxUndo txundo; - tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i)); + UpdateCoins(tx, state, view, txundo, pindex->nHeight, GetTxHash(i)); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); @@ -1683,8 +1687,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi if (fBenchmark) printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees))); + if (GetValueOut(vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(vtx[0]), GetBlockValue(pindex->nHeight, nFees))); if (!control.Wait()) return state.DoS(100, false); @@ -1846,7 +1850,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) BOOST_FOREACH(CTransaction& tx, vResurrect) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - tx.AcceptToMemoryPool(stateDummy, true, false); + mempool.accept(stateDummy, tx, true, false, NULL); } // Delete redundant memory transactions that are in the connected branch @@ -2076,7 +2080,7 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction(state)) + if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the @@ -2096,7 +2100,7 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) { - nSigOps += tx.GetLegacySigOpCount(); + nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); @@ -2135,7 +2139,7 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.IsFinal(nHeight, GetBlockTime())) + if (!IsFinalTx(tx, nHeight, GetBlockTime())) return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint @@ -3488,7 +3492,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool fMissingInputs = false; CValidationState state; - if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) + if (mempool.accept(state, tx, true, true, &fMissingInputs)) { RelayTransaction(tx, inv.hash, vMsg); mapAlreadyAskedFor.erase(inv); @@ -3511,7 +3515,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) CValidationState stateDummy; - if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) + if (mempool.accept(stateDummy, tx, true, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str()); RelayTransaction(tx, inv.hash, vMsg); @@ -4194,7 +4198,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { CTransaction& tx = (*mi).second; - if (tx.IsCoinBase() || !tx.IsFinal()) + if (tx.IsCoinBase() || !IsFinalTx(tx)) continue; COrphan* porphan = NULL; @@ -4249,7 +4253,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an // incentive to create smaller transactions. - double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); + double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0); if (porphan) { @@ -4285,7 +4289,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) continue; // Legacy limits on sigOps: - unsigned int nTxSigOps = tx.GetLegacySigOpCount(); + unsigned int nTxSigOps = GetLegacySigOpCount(tx); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; @@ -4303,22 +4307,22 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); } - if (!tx.HaveInputs(view)) + if (!view.HaveInputs(tx)) continue; - int64 nTxFees = tx.GetValueIn(view)-tx.GetValueOut(); + int64 nTxFees = view.GetValueIn(tx)-GetValueOut(tx); - nTxSigOps += tx.GetP2SHSigOpCount(view); + nTxSigOps += GetP2SHSigOpCount(tx, view); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; CValidationState state; - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) + if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; uint256 hash = tx.GetHash(); - tx.UpdateCoins(state, view, txundo, pindexPrev->nHeight+1, hash); + UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash); // Added pblock->vtx.push_back(tx); @@ -4366,7 +4370,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; - pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); + pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; diff --git a/src/main.h b/src/main.h index c3c7ee3966..7069d4bab3 100644 --- a/src/main.h +++ b/src/main.h @@ -254,155 +254,6 @@ struct CDiskTxPos : public CDiskBlockPos - -/** An input of a transaction. It contains the location of the previous - * transaction's output that it claims and a signature that matches the - * output's public key. - */ -class CTxIn -{ -public: - COutPoint prevout; - CScript scriptSig; - unsigned int nSequence; - - CTxIn() - { - nSequence = std::numeric_limits::max(); - } - - explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = prevoutIn; - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = COutPoint(hashPrevTx, nOut); - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); - ) - - bool IsFinal() const - { - return (nSequence == std::numeric_limits::max()); - } - - friend bool operator==(const CTxIn& a, const CTxIn& b) - { - return (a.prevout == b.prevout && - a.scriptSig == b.scriptSig && - a.nSequence == b.nSequence); - } - - friend bool operator!=(const CTxIn& a, const CTxIn& b) - { - return !(a == b); - } - - std::string ToString() const - { - std::string str; - str += "CTxIn("; - str += prevout.ToString(); - if (prevout.IsNull()) - str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); - else - str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); - if (nSequence != std::numeric_limits::max()) - str += strprintf(", nSequence=%u", nSequence); - str += ")"; - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -/** An output of a transaction. It contains the public key that the next input - * must be able to sign with to claim it. - */ -class CTxOut -{ -public: - int64 nValue; - CScript scriptPubKey; - - CTxOut() - { - SetNull(); - } - - CTxOut(int64 nValueIn, CScript scriptPubKeyIn) - { - nValue = nValueIn; - scriptPubKey = scriptPubKeyIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(nValue); - READWRITE(scriptPubKey); - ) - - void SetNull() - { - nValue = -1; - scriptPubKey.clear(); - } - - bool IsNull() const - { - return (nValue == -1); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - friend bool operator==(const CTxOut& a, const CTxOut& b) - { - return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); - } - - friend bool operator!=(const CTxOut& a, const CTxOut& b) - { - return !(a == b); - } - - bool IsDust() const; - - std::string ToString() const - { - if (scriptPubKey.size() < 6) - return "CTxOut(error)"; - return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - enum GetMinFee_mode { GMF_BLOCK, @@ -412,215 +263,71 @@ enum GetMinFee_mode int64 GetMinFee(const CTransaction& tx, unsigned int nBlockSize = 1, bool fAllowFree = true, enum GetMinFee_mode mode = GMF_BLOCK); -/** The basic transaction that is broadcasted on the network and contained in - * blocks. A transaction can contain multiple inputs and outputs. - */ -class CTransaction -{ -public: - static int64 nMinTxFee; - static int64 nMinRelayTxFee; - static const int CURRENT_VERSION=1; - int nVersion; - std::vector vin; - std::vector vout; - unsigned int nLockTime; - - CTransaction() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); - ) - - void SetNull() - { - nVersion = CTransaction::CURRENT_VERSION; - vin.clear(); - vout.clear(); - nLockTime = 0; - } - - bool IsNull() const - { - return (vin.empty() && vout.empty()); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const - { - // Time based nLockTime implemented in 0.1.6 - if (nLockTime == 0) - return true; - if (nBlockHeight == 0) - nBlockHeight = nBestHeight; - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) - return true; - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.IsFinal()) - return false; - return true; - } - - bool IsNewerThan(const CTransaction& old) const - { - if (vin.size() != old.vin.size()) - return false; - for (unsigned int i = 0; i < vin.size(); i++) - if (vin[i].prevout != old.vin[i].prevout) - return false; - - bool fNewer = false; - unsigned int nLowest = std::numeric_limits::max(); - for (unsigned int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != old.vin[i].nSequence) - { - if (vin[i].nSequence <= nLowest) - { - fNewer = false; - nLowest = vin[i].nSequence; - } - if (old.vin[i].nSequence < nLowest) - { - fNewer = true; - nLowest = old.vin[i].nSequence; - } - } - } - return fNewer; - } - - bool IsCoinBase() const - { - return (vin.size() == 1 && vin[0].prevout.IsNull()); - } - - /** Check for standard transaction types - @return True if all outputs (scriptPubKeys) use only standard transaction forms - */ - bool IsStandard() const; +// +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// /** Check for standard transaction types - @param[in] mapInputs Map of previous transactions that have outputs we're spending + @param[in] mapInputs Map of previous transactions that have outputs we're spending @return True if all inputs (scriptSigs) use only standard transaction forms */ - bool AreInputsStandard(CCoinsViewCache& mapInputs) const; +bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs); - /** Count ECDSA signature operations the old-fashioned (pre-0.6) way - @return number of sigops this transaction's outputs will produce when spent - */ - unsigned int GetLegacySigOpCount() const; +/** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @return number of sigops this transaction's outputs will produce when spent + @see CTransaction::FetchInputs +*/ +unsigned int GetLegacySigOpCount(const CTransaction& tx); - /** Count ECDSA signature operations in pay-to-script-hash inputs. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return maximum number of sigops required to validate this transaction's inputs - */ - unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; - - /** Amount of bitcoins spent by this transaction. - @return sum of all outputs (note: does not include fees) - */ - int64 GetValueOut() const - { - int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); - } - return nValueOut; - } - - /** Amount of bitcoins coming in to this transaction - Note that lightweight clients may not know anything besides the hash of previous transactions, - so may not be able to calculate this. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return Sum of value of all inputs (scriptSigs) - */ - int64 GetValueIn(CCoinsViewCache& mapInputs) const; - - static bool AllowFree(double dPriority) - { - // Large (in bytes) low-priority (new, small-coin) transactions - // need a fee. - return dPriority > COIN * 144 / 250; - } - - friend bool operator==(const CTransaction& a, const CTransaction& b) - { - return (a.nVersion == b.nVersion && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); - } - - friend bool operator!=(const CTransaction& a, const CTransaction& b) - { - return !(a == b); - } +/** Count ECDSA signature operations in pay-to-script-hash inputs. + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return maximum number of sigops required to validate this transaction's inputs + @see CTransaction::FetchInputs + */ +unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& mapInputs); - std::string ToString() const - { - std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", - GetHash().ToString().c_str(), - nVersion, - vin.size(), - vout.size(), - nLockTime); - for (unsigned int i = 0; i < vin.size(); i++) - str += " " + vin[i].ToString() + "\n"; - for (unsigned int i = 0; i < vout.size(); i++) - str += " " + vout[i].ToString() + "\n"; - return str; - } - void print() const - { - printf("%s", ToString().c_str()); - } +inline bool AllowFree(double dPriority) +{ + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > COIN * 144 / 250; +} +// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) +// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it +// instead of being performed inline. +bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, + std::vector *pvChecks = NULL); - // Check whether all prevouts of this transaction are present in the UTXO set represented by view - bool HaveInputs(CCoinsViewCache &view) const; +// Apply the effects of this transaction on the UTXO set represented by view +bool UpdateCoins(const CTransaction& tx, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash); - // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) - // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it - // instead of being performed inline. - bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, - unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, - std::vector *pvChecks = NULL) const; +// Context-independent validity checks +bool CheckTransaction(const CTransaction& tx, CValidationState& state); - // Apply the effects of this transaction on the UTXO set represented by view - void UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; +/** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms +*/ +bool IsStandardTx(const CTransaction& tx); - // Context-independent validity checks - bool CheckTransaction(CValidationState &state) const; +bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = 0); - // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); +/** Amount of bitcoins spent by the transaction. + @return sum of all outputs (note: does not include fees) + */ +int64 GetValueOut(const CTransaction& tx); -protected: - static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); -}; /** wrapper for CTxOut that provides a more compact serialization */ class CTxOutCompressor @@ -2132,6 +1839,21 @@ public: // Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize(); + /** Amount of bitcoins coming in to a transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @param[in] tx transaction for which we are checking input total + @return Sum of value of all inputs (scriptSigs) + @see CTransaction::FetchInputs + */ + int64 GetValueIn(const CTransaction& tx); + + // Check whether all prevouts of the transaction are present in the UTXO set represented by this view + bool HaveInputs(const CTransaction& tx); + + const CTxOut &GetOutputFor(const CTxIn& input); + private: std::map::iterator FetchCoins(const uint256 &txid); }; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e4dbf9c0d9..aeef721ce5 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -12,7 +12,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { - if (!wtx.IsFinal()) + if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1); @@ -186,7 +186,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "
"; } - int64 nTxFee = nDebit - wtx.GetValueOut(); + int64 nTxFee = nDebit - GetValueOut(wtx); if (nTxFee > 0) strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nTxFee) + "
"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 40a5f735cd..e954508769 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -89,7 +89,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - int64 nTxFee = nDebit - wtx.GetValueOut(); + int64 nTxFee = nDebit - GetValueOut(wtx); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { @@ -162,7 +162,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = nBestHeight; - if (!wtx.IsFinal()) + if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index c1e05466e5..917c2f5de9 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -555,7 +555,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHave) { // push to local node CValidationState state; - if (!tx.AcceptToMemoryPool(state, true, false)) + if (!mempool.accept(state, tx, true, false, NULL)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index fbad1944de..585bdb2bfb 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -408,7 +408,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -454,7 +454,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -478,7 +478,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) + if (!IsFinalTx(wtx)) continue; int64 nReceived, nSent, nFee; @@ -839,7 +839,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) continue; int nDepth = wtx.GetDepthInMainChain(); @@ -1220,7 +1220,7 @@ Value gettransaction(const Array& params, bool fHelp) int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + int64 nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe()) diff --git a/src/script.h b/src/script.h index 3cbb2cf322..f963467c94 100644 --- a/src/script.h +++ b/src/script.h @@ -533,7 +533,7 @@ public: bool IsPayToScriptHash() const; - // Called by CTransaction::IsStandard + // Called by IsStandardTx bool IsPushOnly() const { const_iterator pc = begin(); diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 81e77b7df8..a26f4a87db 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -23,7 +23,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", true], -["Tests for CTransaction::CheckTransaction()"], +["Tests for CheckTransaction()"], ["No inputs"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], "0100000000010000000000000000015100000000", true], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 8aeb30f35b..faf911a97f 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -50,7 +50,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", true], -["Tests for CTransaction::CheckTransaction()"], +["Tests for CheckTransaction()"], ["MAX_MONEY output"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", true], diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 65f0ad0cdc..23cb3a8e0a 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(sign) txFrom.vout[i+4].scriptPubKey = standardScripts[i]; txFrom.vout[i+4].nValue = COIN; } - BOOST_CHECK(txFrom.IsStandard()); + BOOST_CHECK(IsStandardTx(txFrom)); CTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(set) txFrom.vout[i].scriptPubKey = outer[i]; txFrom.vout[i].nValue = CENT; } - BOOST_CHECK(txFrom.IsStandard()); + BOOST_CHECK(IsStandardTx(txFrom)); CTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) @@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(set) for (int i = 0; i < 4; i++) { BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); - BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i)); + BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i]), strprintf("txTo[%d].IsStandard", i)); } } @@ -305,15 +305,15 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[2].prevout.hash = txFrom.GetHash(); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); - BOOST_CHECK(txTo.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1U); + BOOST_CHECK(::AreInputsStandard(txTo, coins)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 1U); // Make sure adding crap to the scriptSigs makes them non-standard: for (int i = 0; i < 3; i++) { CScript t = txTo.vin[i].scriptSig; txTo.vin[i].scriptSig = (CScript() << 11) + t; - BOOST_CHECK(!txTo.AreInputsStandard(coins)); + BOOST_CHECK(!::AreInputsStandard(txTo, coins)); txTo.vin[i].scriptSig = t; } @@ -329,11 +329,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd.vin[1].prevout.hash = txFrom.GetHash(); txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); - BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11U); + BOOST_CHECK(!::AreInputsStandard(txToNonStd, coins)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd, coins), 11U); txToNonStd.vin[0].scriptSig.clear(); - BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); + BOOST_CHECK(!::AreInputsStandard(txToNonStd, coins)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index ddff2acd4e..53d1307b69 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(state), strTest); + BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); BOOST_CHECK(state.IsValid()); for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) stream >> tx; CValidationState state; - fValid = tx.CheckTransaction(state) && state.IsValid(); + fValid = CheckTransaction(tx, state) && state.IsValid(); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -163,11 +163,11 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CTransaction tx; stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(state) && state.IsValid(), "Simple deserialized transaction should be valid."); + BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail tx.vin.push_back(tx.vin[0]); - BOOST_CHECK_MESSAGE(!tx.CheckTransaction(state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); } // @@ -230,16 +230,16 @@ BOOST_AUTO_TEST_CASE(test_Get) t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - BOOST_CHECK(t1.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(t1.GetValueIn(coins), (50+21+22)*CENT); + BOOST_CHECK(AreInputsStandard(t1, coins)); + BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT); // Adding extra junk to the scriptSig should make it non-standard: t1.vin[0].scriptSig << OP_11; - BOOST_CHECK(!t1.AreInputsStandard(coins)); + BOOST_CHECK(!AreInputsStandard(t1, coins)); // ... as should not having enough: t1.vin[0].scriptSig = CScript(); - BOOST_CHECK(!t1.AreInputsStandard(coins)); + BOOST_CHECK(!AreInputsStandard(t1, coins)); } BOOST_AUTO_TEST_CASE(test_IsStandard) @@ -260,16 +260,16 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) key.MakeNewKey(true); t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - BOOST_CHECK(t.IsStandard()); + BOOST_CHECK(IsStandardTx(t)); t.vout[0].nValue = 5011; // dust - BOOST_CHECK(!t.IsStandard()); + BOOST_CHECK(!IsStandardTx(t)); t.vout[0].nValue = 6011; // not dust - BOOST_CHECK(t.IsStandard()); + BOOST_CHECK(IsStandardTx(t)); t.vout[0].scriptPubKey = CScript() << OP_1; - BOOST_CHECK(!t.IsStandard()); + BOOST_CHECK(!IsStandardTx(t)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet.cpp b/src/wallet.cpp index da75752bb4..7041d49dab 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -643,7 +643,7 @@ void CWalletTx::GetAmounts(list >& listReceived, int64 nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - int64 nValueOut = GetValueOut(); + int64 nValueOut = GetValueOut(*this); nFee = nDebit - nValueOut; } @@ -933,7 +933,7 @@ int64 CWallet::GetUnconfirmedBalance() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) nTotal += pcoin->GetAvailableCredit(); } } @@ -965,7 +965,7 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal()) + if (!IsFinalTx(*pcoin)) continue; if (fOnlyConfirmed && !pcoin->IsConfirmed()) @@ -1178,7 +1178,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) { CTxOut txout(s.second, s.first); - if (txout.IsDust()) + if (txout.IsDust(CTransaction::nMinRelayTxFee)) { strFailReason = _("Transaction amount too small"); return false; @@ -1237,7 +1237,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust()) + if (newTxOut.IsDust(CTransaction::nMinRelayTxFee)) { nFeeRet += nChange; reservekey.ReturnKey(); @@ -1276,7 +1276,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - bool fAllowFree = CTransaction::AllowFree(dPriority); + bool fAllowFree = AllowFree(dPriority); int64 nMinFee = GetMinFee(wtxNew, 1, fAllowFree, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { @@ -1657,7 +1657,7 @@ std::map CWallet::GetAddressBalances() { CWalletTx *pcoin = &walletEntry.second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) diff --git a/src/wallet.h b/src/wallet.h index 22dce78e91..674bae66dd 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -641,7 +641,7 @@ public: bool IsConfirmed() const { // Quick answer in most cases - if (!IsFinal()) + if (!IsFinalTx(*this)) return false; if (GetDepthInMainChain() >= 1) return true; @@ -658,7 +658,7 @@ public: { const CMerkleTx* ptx = vWorkQueue[i]; - if (!ptx->IsFinal()) + if (!IsFinalTx(*ptx)) return false; if (ptx->GetDepthInMainChain() >= 1) continue; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 4a73413d26..8910cac4bc 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -204,7 +204,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; CValidationState state; - if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid()) + if (CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()) wtx.BindWallet(pwallet); else { -- cgit v1.2.3 From 65e7bbef74f7077a5b46943b1556d66d84955f61 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 08:45:47 -0800 Subject: Moved CCoins, CTxOutCompressor, CTxInUndo, and CTxUndo to core. --- src/core.h | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.h | 330 ------------------------------------------------------------ 2 files changed, 332 insertions(+), 330 deletions(-) diff --git a/src/core.h b/src/core.h index 21ca6618a2..c86e1f499d 100644 --- a/src/core.h +++ b/src/core.h @@ -340,4 +340,336 @@ public: } }; +/** wrapper for CTxOut that provides a more compact serialization */ +class CTxOutCompressor +{ +private: + CTxOut &txout; + +public: + static uint64 CompressAmount(uint64 nAmount); + static uint64 DecompressAmount(uint64 nAmount); + + CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } + + IMPLEMENT_SERIALIZE(({ + if (!fRead) { + uint64 nVal = CompressAmount(txout.nValue); + READWRITE(VARINT(nVal)); + } else { + uint64 nVal = 0; + READWRITE(VARINT(nVal)); + txout.nValue = DecompressAmount(nVal); + } + CScriptCompressor cscript(REF(txout.scriptPubKey)); + READWRITE(cscript); + });) +}; + +/** Undo information for a CTxIn + * + * Contains the prevout's CTxOut being spent, and if this was the + * last output of the affected transaction, its metadata as well + * (coinbase or not, height, transaction version) + */ +class CTxInUndo +{ +public: + CTxOut txout; // the txout data before being spent + bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase + unsigned int nHeight; // if the outpoint was the last unspent: its height + int nVersion; // if the outpoint was the last unspent: its version + + CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} + CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + + (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + + ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); + if (nHeight > 0) + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); + } + + template + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + ::Unserialize(s, VARINT(nCode), nType, nVersion); + nHeight = nCode / 2; + fCoinBase = nCode & 1; + if (nHeight > 0) + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); + } +}; + +/** Undo information for a CTransaction */ +class CTxUndo +{ +public: + // undo information for all txins + std::vector vprevout; + + IMPLEMENT_SERIALIZE( + READWRITE(vprevout); + ) +}; + + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector vout; + + // at which height this transaction was included in the active block chain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + int nVersion; + + // construct a CCoins from a CTransaction, at a given height + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + if (vout.empty()) + std::vector().swap(vout); + } + + void swap(CCoins &to) { + std::swap(to.fCoinBase, fCoinBase); + to.vout.swap(vout); + std::swap(to.nHeight, nHeight); + std::swap(to.nVersion, nVersion); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + // calculate number of bytes for the bitmask, and its number of non-zero bytes + // each bit in the bitmask represents the availability of one output, but the + // availabilities of the first two outputs are encoded separately + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; + } + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; + } + + // mark a vout spent + bool Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); + } + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; + + #endif diff --git a/src/main.h b/src/main.h index 7069d4bab3..1279e62ca8 100644 --- a/src/main.h +++ b/src/main.h @@ -328,88 +328,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = */ int64 GetValueOut(const CTransaction& tx); - -/** wrapper for CTxOut that provides a more compact serialization */ -class CTxOutCompressor -{ -private: - CTxOut &txout; - -public: - static uint64 CompressAmount(uint64 nAmount); - static uint64 DecompressAmount(uint64 nAmount); - - CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } - - IMPLEMENT_SERIALIZE(({ - if (!fRead) { - uint64 nVal = CompressAmount(txout.nValue); - READWRITE(VARINT(nVal)); - } else { - uint64 nVal = 0; - READWRITE(VARINT(nVal)); - txout.nValue = DecompressAmount(nVal); - } - CScriptCompressor cscript(REF(txout.scriptPubKey)); - READWRITE(cscript); - });) -}; - -/** Undo information for a CTxIn - * - * Contains the prevout's CTxOut being spent, and if this was the - * last output of the affected transaction, its metadata as well - * (coinbase or not, height, transaction version) - */ -class CTxInUndo -{ -public: - CTxOut txout; // the txout data before being spent - bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase - unsigned int nHeight; // if the outpoint was the last unspent: its height - int nVersion; // if the outpoint was the last unspent: its version - - CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {} - CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) + - (nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) + - ::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion); - } - - template - void Serialize(Stream &s, int nType, int nVersion) const { - ::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion); - if (nHeight > 0) - ::Serialize(s, VARINT(this->nVersion), nType, nVersion); - ::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion); - } - - template - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nCode = 0; - ::Unserialize(s, VARINT(nCode), nType, nVersion); - nHeight = nCode / 2; - fCoinBase = nCode & 1; - if (nHeight > 0) - ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); - ::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion); - } -}; - -/** Undo information for a CTransaction */ -class CTxUndo -{ -public: - // undo information for all txins - std::vector vprevout; - - IMPLEMENT_SERIALIZE( - READWRITE(vprevout); - ) -}; - /** Undo information for a CBlock */ class CBlockUndo { @@ -480,254 +398,6 @@ public: } }; -/** pruned version of CTransaction: only retains metadata and unspent transaction outputs - * - * Serialized format: - * - VARINT(nVersion) - * - VARINT(nCode) - * - unspentness bitvector, for vout[2] and further; least significant byte first - * - the non-spent CTxOuts (via CTxOutCompressor) - * - VARINT(nHeight) - * - * The nCode value consists of: - * - bit 1: IsCoinBase() - * - bit 2: vout[0] is not spent - * - bit 4: vout[1] is not spent - * - The higher bits encode N, the number of non-zero bytes in the following bitvector. - * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at - * least one non-spent output). - * - * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e - * <><><--------------------------------------------><----> - * | \ | / - * version code vout[1] height - * - * - version = 1 - * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) - * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 - * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 - * * 8358: compact amount representation for 60000000000 (600 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 - * - height = 203998 - * - * - * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b - * <><><--><--------------------------------------------------><----------------------------------------------><----> - * / \ \ | | / - * version code unspentness vout[4] vout[16] height - * - * - version = 1 - * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, - * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) - * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent - * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee - * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 - * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 - * * bbd123: compact amount representation for 110397 (0.001 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 - * - height = 120891 - */ -class CCoins -{ -public: - // whether transaction is a coinbase - bool fCoinBase; - - // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped - std::vector vout; - - // at which height this transaction was included in the active block chain - int nHeight; - - // version of the CTransaction; accesses to this value should probably check for nHeight as well, - // as new tx version will probably only be introduced at certain heights - int nVersion; - - // construct a CCoins from a CTransaction, at a given height - CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } - - // empty constructor - CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } - - // remove spent outputs at the end of vout - void Cleanup() { - while (vout.size() > 0 && vout.back().IsNull()) - vout.pop_back(); - if (vout.empty()) - std::vector().swap(vout); - } - - void swap(CCoins &to) { - std::swap(to.fCoinBase, fCoinBase); - to.vout.swap(vout); - std::swap(to.nHeight, nHeight); - std::swap(to.nVersion, nVersion); - } - - // equality test - friend bool operator==(const CCoins &a, const CCoins &b) { - return a.fCoinBase == b.fCoinBase && - a.nHeight == b.nHeight && - a.nVersion == b.nVersion && - a.vout == b.vout; - } - friend bool operator!=(const CCoins &a, const CCoins &b) { - return !(a == b); - } - - // calculate number of bytes for the bitmask, and its number of non-zero bytes - // each bit in the bitmask represents the availability of one output, but the - // availabilities of the first two outputs are encoded separately - void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { - unsigned int nLastUsedByte = 0; - for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { - bool fZero = true; - for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { - if (!vout[2+b*8+i].IsNull()) { - fZero = false; - continue; - } - } - if (!fZero) { - nLastUsedByte = b + 1; - nNonzeroBytes++; - } - } - nBytes += nLastUsedByte; - } - - bool IsCoinBase() const { - return fCoinBase; - } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - unsigned int nSize = 0; - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); - // size of header code - nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); - // spentness bitmask - nSize += nMaskSize; - // txouts themself - for (unsigned int i = 0; i < vout.size(); i++) - if (!vout[i].IsNull()) - nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); - // height - nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); - return nSize; - } - - template - void Serialize(Stream &s, int nType, int nVersion) const { - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - ::Serialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Serialize(s, VARINT(nCode), nType, nVersion); - // spentness bitmask - for (unsigned int b = 0; b - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nCode = 0; - // version - ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Unserialize(s, VARINT(nCode), nType, nVersion); - fCoinBase = nCode & 1; - std::vector vAvail(2, false); - vAvail[0] = nCode & 2; - vAvail[1] = nCode & 4; - unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); - // spentness bitmask - while (nMaskCode > 0) { - unsigned char chAvail = 0; - ::Unserialize(s, chAvail, nType, nVersion); - for (unsigned int p = 0; p < 8; p++) { - bool f = (chAvail & (1 << p)) != 0; - vAvail.push_back(f); - } - if (chAvail != 0) - nMaskCode--; - } - // txouts themself - vout.assign(vAvail.size(), CTxOut()); - for (unsigned int i = 0; i < vAvail.size(); i++) { - if (vAvail[i]) - ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); - } - // coinbase height - ::Unserialize(s, VARINT(nHeight), nType, nVersion); - Cleanup(); - } - - // mark an outpoint spent, and construct undo information - bool Spend(const COutPoint &out, CTxInUndo &undo) { - if (out.n >= vout.size()) - return false; - if (vout[out.n].IsNull()) - return false; - undo = CTxInUndo(vout[out.n]); - vout[out.n].SetNull(); - Cleanup(); - if (vout.size() == 0) { - undo.nHeight = nHeight; - undo.fCoinBase = fCoinBase; - undo.nVersion = this->nVersion; - } - return true; - } - - // mark a vout spent - bool Spend(int nPos) { - CTxInUndo undo; - COutPoint out(0, nPos); - return Spend(out, undo); - } - - // check whether a particular output is still available - bool IsAvailable(unsigned int nPos) const { - return (nPos < vout.size() && !vout[nPos].IsNull()); - } - - // check whether the entire CCoins is spent - // note that only !IsPruned() CCoins can be serialized - bool IsPruned() const { - BOOST_FOREACH(const CTxOut &out, vout) - if (!out.IsNull()) - return false; - return true; - } -}; /** Closure representing one script verification * Note that this stores references to the spending transaction */ -- cgit v1.2.3 From 48343a0a506398e22349c791717500a20bfff511 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 09:05:13 -0800 Subject: Removed script.cpp's dependence on main.h --- src/script.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script.cpp b/src/script.cpp index b411666353..cf6eeaf392 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -9,10 +9,10 @@ using namespace std; using namespace boost; #include "script.h" +#include "core.h" #include "keystore.h" #include "bignum.h" #include "key.h" -#include "main.h" #include "sync.h" #include "util.h" -- cgit v1.2.3 From aabdf9e899e9024927c26c194ad38142495f80bf Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 14:58:06 -0800 Subject: Moved UpdateTime out of CBlockHeader and moved CBlockHeader into core. --- src/core.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 10 ++++----- src/main.h | 66 +++---------------------------------------------------- src/rpcmining.cpp | 4 ++-- 4 files changed, 71 insertions(+), 70 deletions(-) diff --git a/src/core.h b/src/core.h index c86e1f499d..cba3ab233f 100644 --- a/src/core.h +++ b/src/core.h @@ -672,4 +672,65 @@ public: }; +/** Nodes collect new transactions into a block, hash them into a hash tree, + * and scan through nonce values to make the block's hash satisfy proof-of-work + * requirements. When they solve the proof-of-work, they broadcast the block + * to everyone and the block is added to the block chain. The first transaction + * in the block is a special one that creates a new coin owned by the creator + * of the block. + */ +class CBlockHeader +{ +public: + // header + static const int CURRENT_VERSION=2; + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + CBlockHeader() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + void SetNull() + { + nVersion = CBlockHeader::CURRENT_VERSION; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + return Hash(BEGIN(nVersion), END(nNonce)); + } + + int64 GetBlockTime() const + { + return (int64)nTime; + } +}; + #endif diff --git a/src/main.cpp b/src/main.cpp index 28b32101d3..bd40476d4c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1300,13 +1300,13 @@ bool ConnectBestBlock(CValidationState &state) { } while(true); } -void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) +void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) { - nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + block.nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); // Updating time can change work required on testnet: if (fTestNet) - nBits = GetNextWorkRequired(pindexPrev, this); + block.nBits = GetNextWorkRequired(pindexPrev, &block); } @@ -4366,7 +4366,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->UpdateTime(pindexPrev); + UpdateTime(*pblock, pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; @@ -4606,7 +4606,7 @@ void static BitcoinMiner(CWallet *pwallet) break; // Update nTime every few seconds - pblock->UpdateTime(pindexPrev); + UpdateTime(*pblock, pindexPrev); nBlockTime = ByteReverse(pblock->nTime); if (fTestNet) { diff --git a/src/main.h b/src/main.h index 1279e62ca8..f4e80c85ce 100644 --- a/src/main.h +++ b/src/main.h @@ -180,6 +180,9 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, b bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); /** Find the best known block, and make it the tip of the block chain */ bool ConnectBestBlock(CValidationState &state); + +void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev); + /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); /** Verify a signature */ @@ -576,69 +579,6 @@ public: }; -/** Nodes collect new transactions into a block, hash them into a hash tree, - * and scan through nonce values to make the block's hash satisfy proof-of-work - * requirements. When they solve the proof-of-work, they broadcast the block - * to everyone and the block is added to the block chain. The first transaction - * in the block is a special one that creates a new coin owned by the creator - * of the block. - */ -class CBlockHeader -{ -public: - // header - static const int CURRENT_VERSION=2; - int nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; - - CBlockHeader() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); - ) - - void SetNull() - { - nVersion = CBlockHeader::CURRENT_VERSION; - hashPrevBlock = 0; - hashMerkleRoot = 0; - nTime = 0; - nBits = 0; - nNonce = 0; - } - - bool IsNull() const - { - return (nBits == 0); - } - - uint256 GetHash() const - { - return Hash(BEGIN(nVersion), END(nNonce)); - } - - int64 GetBlockTime() const - { - return (int64)nTime; - } - - void UpdateTime(const CBlockIndex* pindexPrev); -}; - class CBlock : public CBlockHeader { public: diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index d2cac68706..6f8ac7a112 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -159,7 +159,7 @@ Value getwork(const Array& params, bool fHelp) CBlock* pblock = &pblocktemplate->block; // pointer for convenience // Update nTime - pblock->UpdateTime(pindexPrev); + UpdateTime(*pblock, pindexPrev); pblock->nNonce = 0; // Update nExtraNonce @@ -289,7 +289,7 @@ Value getblocktemplate(const Array& params, bool fHelp) CBlock* pblock = &pblocktemplate->block; // pointer for convenience // Update nTime - pblock->UpdateTime(pindexPrev); + UpdateTime(*pblock, pindexPrev); pblock->nNonce = 0; Array transactions; -- cgit v1.2.3 From 501da2503a39dd88470df89ccc923c696fe6b111 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Wed, 5 Jun 2013 20:21:41 -0700 Subject: Using boost::signals2 to message main from net.cpp. --- src/init.cpp | 6 ++---- src/main.cpp | 15 +++++++++++++++ src/main.h | 5 +++++ src/net.cpp | 30 ++++++------------------------ src/net.h | 18 +++++++++--------- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index a1dbfd2a5f..f8b2b23fd0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -571,10 +571,8 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 6: network initialization - SetProcessMessagesHandler(ProcessMessages); - SetSendMessagesHandler(SendMessages); - SetStartShutdownHandler(StartShutdown); - + RegisterNodeSignals(GetNodeSignals()); + int nSocksVersion = GetArg("-socks", 5); if (nSocksVersion != 4 && nSocksVersion != 5) return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion)); diff --git a/src/main.cpp b/src/main.cpp index bd40476d4c..98921e1423 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -160,7 +160,22 @@ void static ResendWalletTransactions() +////////////////////////////////////////////////////////////////////////////// +// +// Registration of network node signals. +// + +void RegisterNodeSignals(CNodeSignals& nodeSignals) +{ + nodeSignals.ProcessMessages.connect(&ProcessMessages); + nodeSignals.SendMessages.connect(&SendMessages); +} +void UnregisterNodeSignals(CNodeSignals& nodeSignals) +{ + nodeSignals.ProcessMessages.disconnect(&ProcessMessages); + nodeSignals.SendMessages.disconnect(&SendMessages); +} diff --git a/src/main.h b/src/main.h index f4e80c85ce..f62cfbef6d 100644 --- a/src/main.h +++ b/src/main.h @@ -124,6 +124,11 @@ void UnregisterWallet(CWallet* pwalletIn); /** Push an updated transaction to all registered wallets */ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +/** Register with a network node to receive its signals */ +void RegisterNodeSignals(CNodeSignals& nodeSignals); +/** Unregister a network node */ +void UnregisterNodeSignals(CNodeSignals& nodeSignals); + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd); /** Process an incoming block */ diff --git a/src/net.cpp b/src/net.cpp index 5932f5f8e3..dc9a7e5371 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -68,27 +68,9 @@ CCriticalSection cs_vAddedNodes; static CSemaphore *semOutbound = NULL; -// -// Handlers that need to be registered -// -static ProcessMessagesHandler fnProcessMessages = NULL; -static SendMessagesHandler fnSendMessages = NULL; -static StartShutdownHandler fnStartShutdown = NULL; - -void SetProcessMessagesHandler(ProcessMessagesHandler handler) -{ - fnProcessMessages = handler; -} - -void SetSendMessagesHandler(SendMessagesHandler handler) -{ - fnSendMessages = handler; -} - -void SetStartShutdownHandler(StartShutdownHandler handler) -{ - fnStartShutdown = handler; -} +// Signals for message handling +static CNodeSignals g_signals; +CNodeSignals& GetNodeSignals() { return g_signals; } void AddOneShot(string strDest) { @@ -1646,7 +1628,7 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) - if (!ProcessMessages(pnode)) + if (!g_signals.ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); } boost::this_thread::interruption_point(); @@ -1654,8 +1636,8 @@ void ThreadMessageHandler() // Send messages { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend && fnSendMessages) - fnSendMessages(pnode, pnode == pnodeTrickle); + if (lockSend) + g_signals.SendMessages(pnode, pnode == pnodeTrickle); } boost::this_thread::interruption_point(); } diff --git a/src/net.h b/src/net.h index 1c8c99aa3b..fdac60d9ce 100644 --- a/src/net.h +++ b/src/net.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #ifndef WIN32 @@ -45,16 +46,15 @@ void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); -// -// Handlers that require registration -// -typedef bool (*ProcessMessagesHandler)(CNode* pfrom); -typedef bool (*SendMessagesHandler)(CNode* pto, bool fSendTrickle); -typedef void (*StartShutdownHandler)(); +// Signals for message handling +struct CNodeSignals +{ + boost::signals2::signal ProcessMessages; + boost::signals2::signal SendMessages; +}; + +CNodeSignals& GetNodeSignals(); -void SetProcessMessagesHandler(ProcessMessagesHandler handler); -void SetSendMessagesHandler(SendMessagesHandler handler); -void SetStartShutdownHandler(StartShutdownHandler handler); enum { -- cgit v1.2.3 From 6e68524e95da2bedc21b1d95c4a206b902ab7c22 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Thu, 6 Jun 2013 00:04:33 -0700 Subject: Removed the main.h include from net.cpp. --- src/main.h | 2 -- src/net.cpp | 8 +------- src/net.h | 3 +++ 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main.h b/src/main.h index f62cfbef6d..f20fad98a4 100644 --- a/src/main.h +++ b/src/main.h @@ -35,8 +35,6 @@ static const unsigned int MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN/5; static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; /** The maximum number of orphan transactions kept in memory */ static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; -/** The maximum number of entries in an 'inv' protocol message */ -static const unsigned int MAX_INV_SZ = 50000; /** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ diff --git a/src/net.cpp b/src/net.cpp index dc9a7e5371..adc5f93029 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -5,7 +5,7 @@ #include "db.h" #include "net.h" -#include "main.h" +#include "core.h" #include "addrman.h" #include "ui_interface.h" #include "script.h" @@ -1567,11 +1567,6 @@ void static StartSync(const vector &vNodes) { CNode *pnodeNewSync = NULL; double dBestScore = 0; - // fImporting and fReindex are accessed out of cs_main here, but only - // as an optimization - they are checked again in SendMessages. - if (fImporting || fReindex) - return; - // Iterate over all nodes BOOST_FOREACH(CNode* pnode, vNodes) { // check preconditions for allowing a sync @@ -1855,7 +1850,6 @@ bool StopNode() { printf("StopNode()\n"); MapPort(false); - nTransactionsUpdated++; if (semOutbound) for (int i=0; ipost(); diff --git a/src/net.h b/src/net.h index fdac60d9ce..e75fe48f64 100644 --- a/src/net.h +++ b/src/net.h @@ -23,6 +23,9 @@ #include "hash.h" #include "bloom.h" +/** The maximum number of entries in an 'inv' protocol message */ +static const unsigned int MAX_INV_SZ = 50000; + class CNode; class CBlockIndex; extern int nBestHeight; -- cgit v1.2.3