From 57153d4e1ac6e2cc9fc3f2fcd6d786e9813d824f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 28 Aug 2014 17:14:22 +0200 Subject: [PATCH 1/2] rpc: Compute number of confirmations of a block from block height Currently this uses a CMerkleTx, but that makes no sense as we have the CBlockIndex available. As noted by @jgarzik. --- src/rpcblockchain.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 58cab1404..8a4b25cc5 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -54,9 +54,11 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) { Object result; result.push_back(Pair("hash", block.GetHash().GetHex())); - CMerkleTx txGen(block.vtx[0]); - txGen.SetMerkleBranch(&block); - result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); + int confirmations = -1; + // Only report confirmations if the block is on the main chain + if (chainActive.Contains(blockindex)) + confirmations = chainActive.Height() - blockindex->nHeight + 1; + result.push_back(Pair("confirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("version", block.nVersion)); @@ -242,7 +244,7 @@ Value getblock(const Array& params, bool fHelp) "\nResult (for verbose = true):\n" "{\n" " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" " \"size\" : n, (numeric) The block size\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" From 0101483f46396a7f1d19a9d29a1da15639ce4233 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 28 Aug 2014 17:15:21 +0200 Subject: [PATCH 2/2] Move CMerkleTx to wallet.cpp/h It is only used by the wallet so it has no place in main. --- src/main.cpp | 99 -------------------------------------------------- src/main.h | 60 ------------------------------ src/wallet.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/wallet.h | 57 +++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 159 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ddc37ea53..6f3a5aae8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -732,53 +732,6 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } -int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -{ - AssertLockHeld(cs_main); - CBlock blockTmp; - - if (pblock == NULL) { - CCoins coins; - if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = chainActive[coins.nHeight]; - if (pindex) { - if (!ReadBlockFromDisk(blockTmp, pindex)) - return 0; - pblock = &blockTmp; - } - } - } - - if (pblock) { - // Update the tx's hashBlock - hashBlock = pblock->GetHash(); - - // Locate the transaction - for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) - if (pblock->vtx[nIndex] == *(CTransaction*)this) - break; - if (nIndex == (int)pblock->vtx.size()) - { - vMerkleBranch.clear(); - nIndex = -1; - LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); - return 0; - } - - // Fill in merkle branch - vMerkleBranch = pblock->GetMerkleBranch(nIndex); - } - - // Is the tx in a block that's in the main chain - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - return chainActive.Height() - pindex->nHeight + 1; -} @@ -1028,58 +981,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } - -int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const -{ - if (hashBlock == 0 || nIndex == -1) - return 0; - AssertLockHeld(cs_main); - - // Find the block it claims to be in - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) - { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; - } - - pindexRet = pindex; - return chainActive.Height() - pindex->nHeight + 1; -} - -int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const -{ - AssertLockHeld(cs_main); - int nResult = GetDepthInMainChainINTERNAL(pindexRet); - if (nResult == 0 && !mempool.exists(GetHash())) - return -1; // Not in chain, not in mempool - - return nResult; -} - -int CMerkleTx::GetBlocksToMaturity() const -{ - if (!IsCoinBase()) - return 0; - return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); -} - - -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee) -{ - CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee); -} - - // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { diff --git a/src/main.h b/src/main.h index 3160b28c4..9fe15d3aa 100644 --- a/src/main.h +++ b/src/main.h @@ -344,66 +344,6 @@ public: } }; -/** A transaction with a merkle branch linking it to the block chain. */ -class CMerkleTx : public CTransaction -{ -private: - int GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const; - -public: - uint256 hashBlock; - std::vector vMerkleBranch; - int nIndex; - - // memory only - mutable bool fMerkleVerified; - - - CMerkleTx() - { - Init(); - } - - CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) - { - Init(); - } - - void Init() - { - hashBlock = 0; - nIndex = -1; - fMerkleVerified = false; - } - - - IMPLEMENT_SERIALIZE - ( - nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); - nVersion = this->nVersion; - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - ) - - - int SetMerkleBranch(const CBlock* pblock=NULL); - - // Return depth of transaction in blockchain: - // -1 : not in blockchain, and not in memory pool (conflicted transaction) - // 0 : in memory pool, waiting to be included in a block - // >=1 : this many blocks deep in the main chain - int GetDepthInMainChain(CBlockIndex* &pindexRet) const; - int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } - bool IsInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } - int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectInsaneFee=true); -}; - - - - - /** Data structure that represents a partial merkle tree. * * It respresents a subset of the txid's of a known block, in a way that diff --git a/src/wallet.cpp b/src/wallet.cpp index 786b2f6a9..18a5b3971 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -2194,3 +2194,102 @@ CWalletKey::CWalletKey(int64_t nExpires) nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; } + +int CMerkleTx::SetMerkleBranch(const CBlock* pblock) +{ + AssertLockHeld(cs_main); + CBlock blockTmp; + + if (pblock == NULL) { + CCoins coins; + if (pcoinsTip->GetCoins(GetHash(), coins)) { + CBlockIndex *pindex = chainActive[coins.nHeight]; + if (pindex) { + if (!ReadBlockFromDisk(blockTmp, pindex)) + return 0; + pblock = &blockTmp; + } + } + } + + if (pblock) { + // Update the tx's hashBlock + hashBlock = pblock->GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) + if (pblock->vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == (int)pblock->vtx.size()) + { + vMerkleBranch.clear(); + nIndex = -1; + LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = pblock->GetMerkleBranch(nIndex); + } + + // Is the tx in a block that's in the main chain + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + return chainActive.Height() - pindex->nHeight + 1; +} + +int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const +{ + if (hashBlock == 0 || nIndex == -1) + return 0; + AssertLockHeld(cs_main); + + // Find the block it claims to be in + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) + { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + pindexRet = pindex; + return chainActive.Height() - pindex->nHeight + 1; +} + +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +{ + AssertLockHeld(cs_main); + int nResult = GetDepthInMainChainINTERNAL(pindexRet); + if (nResult == 0 && !mempool.exists(GetHash())) + return -1; // Not in chain, not in mempool + + return nResult; +} + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee) +{ + CValidationState state; + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee); +} + diff --git a/src/wallet.h b/src/wallet.h index b3878adb1..544b4f5bf 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -457,6 +457,63 @@ struct COutputEntry int vout; }; +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx : public CTransaction +{ +private: + int GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const; + +public: + uint256 hashBlock; + std::vector vMerkleBranch; + int nIndex; + + // memory only + mutable bool fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = 0; + nIndex = -1; + fMerkleVerified = false; + } + + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + + + int SetMerkleBranch(const CBlock* pblock=NULL); + + // Return depth of transaction in blockchain: + // -1 : not in blockchain, and not in memory pool (conflicted transaction) + // 0 : in memory pool, waiting to be included in a block + // >=1 : this many blocks deep in the main chain + int GetDepthInMainChain(CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + bool IsInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectInsaneFee=true); +}; + + /** A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. */