1 // Copyright (c) 2018 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <interfaces/chain.h>
6 
7 #include <chain.h>
8 #include <chainparams.h>
9 #include <primitives/block.h>
10 #include <sync.h>
11 #include <txmempool.h>
12 #include <uint256.h>
13 #include <util/system.h>
14 #include <validation.h>
15 
16 #include <memory>
17 #include <utility>
18 
19 namespace interfaces {
20 namespace {
21 
22 class LockImpl : public Chain::Lock
23 {
getHeight()24     Optional<int> getHeight() override
25     {
26         int height = ::chainActive.Height();
27         if (height >= 0) {
28             return height;
29         }
30         return nullopt;
31     }
getBlockHeight(const uint256 & hash)32     Optional<int> getBlockHeight(const uint256& hash) override
33     {
34         CBlockIndex* block = LookupBlockIndex(hash);
35         if (block && ::chainActive.Contains(block)) {
36             return block->nHeight;
37         }
38         return nullopt;
39     }
getBlockDepth(const uint256 & hash)40     int getBlockDepth(const uint256& hash) override
41     {
42         const Optional<int> tip_height = getHeight();
43         const Optional<int> height = getBlockHeight(hash);
44         return tip_height && height ? *tip_height - *height + 1 : 0;
45     }
getBlockHash(int height)46     uint256 getBlockHash(int height) override
47     {
48         CBlockIndex* block = ::chainActive[height];
49         assert(block != nullptr);
50         return block->GetBlockHash();
51     }
getBlockTime(int height)52     int64_t getBlockTime(int height) override
53     {
54         CBlockIndex* block = ::chainActive[height];
55         assert(block != nullptr);
56         return block->GetBlockTime();
57     }
getBlockMedianTimePast(int height)58     int64_t getBlockMedianTimePast(int height) override
59     {
60         CBlockIndex* block = ::chainActive[height];
61         assert(block != nullptr);
62         return block->GetMedianTimePast();
63     }
haveBlockOnDisk(int height)64     bool haveBlockOnDisk(int height) override
65     {
66         CBlockIndex* block = ::chainActive[height];
67         return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
68     }
findFirstBlockWithTime(int64_t time,uint256 * hash)69     Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
70     {
71         CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
72         if (block) {
73             if (hash) *hash = block->GetBlockHash();
74             return block->nHeight;
75         }
76         return nullopt;
77     }
findFirstBlockWithTimeAndHeight(int64_t time,int height)78     Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
79     {
80         // TODO: Could update CChain::FindEarliestAtLeast() to take a height
81         // parameter and use it with std::lower_bound() to make this
82         // implementation more efficient and allow combining
83         // findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
84         // method.
85         for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
86             if (block->GetBlockTime() >= time) {
87                 return block->nHeight;
88             }
89         }
90         return nullopt;
91     }
findPruned(int start_height,Optional<int> stop_height)92     Optional<int> findPruned(int start_height, Optional<int> stop_height) override
93     {
94         if (::fPruneMode) {
95             CBlockIndex* block = stop_height ? ::chainActive[*stop_height] : ::chainActive.Tip();
96             while (block && block->nHeight >= start_height) {
97                 if ((block->nStatus & BLOCK_HAVE_DATA) == 0) {
98                     return block->nHeight;
99                 }
100                 block = block->pprev;
101             }
102         }
103         return nullopt;
104     }
findFork(const uint256 & hash,Optional<int> * height)105     Optional<int> findFork(const uint256& hash, Optional<int>* height) override
106     {
107         const CBlockIndex* block = LookupBlockIndex(hash);
108         const CBlockIndex* fork = block ? ::chainActive.FindFork(block) : nullptr;
109         if (height) {
110             if (block) {
111                 *height = block->nHeight;
112             } else {
113                 height->reset();
114             }
115         }
116         if (fork) {
117             return fork->nHeight;
118         }
119         return nullopt;
120     }
isPotentialTip(const uint256 & hash)121     bool isPotentialTip(const uint256& hash) override
122     {
123         if (::chainActive.Tip()->GetBlockHash() == hash) return true;
124         CBlockIndex* block = LookupBlockIndex(hash);
125         return block && block->GetAncestor(::chainActive.Height()) == ::chainActive.Tip();
126     }
getTipLocator()127     CBlockLocator getTipLocator() override { return ::chainActive.GetLocator(); }
findLocatorFork(const CBlockLocator & locator)128     Optional<int> findLocatorFork(const CBlockLocator& locator) override
129     {
130         LockAnnotation lock(::cs_main);
131         if (CBlockIndex* fork = FindForkInGlobalIndex(::chainActive, locator)) {
132             return fork->nHeight;
133         }
134         return nullopt;
135     }
136 };
137 
138 class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
139 {
140     using UniqueLock::UniqueLock;
141 };
142 
143 class ChainImpl : public Chain
144 {
145 public:
lock(bool try_lock)146     std::unique_ptr<Chain::Lock> lock(bool try_lock) override
147     {
148         auto result = MakeUnique<LockingStateImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
149         if (try_lock && result && !*result) return {};
150         // std::move necessary on some compilers due to conversion from
151         // LockingStateImpl to Lock pointer
152         return std::move(result);
153     }
assumeLocked()154     std::unique_ptr<Chain::Lock> assumeLocked() override { return MakeUnique<LockImpl>(); }
findBlock(const uint256 & hash,CBlock * block,int64_t * time,int64_t * time_max)155     bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override
156     {
157         CBlockIndex* index;
158         {
159             LOCK(cs_main);
160             index = LookupBlockIndex(hash);
161             if (!index) {
162                 return false;
163             }
164             if (time) {
165                 *time = index->GetBlockTime();
166             }
167             if (time_max) {
168                 *time_max = index->GetBlockTimeMax();
169             }
170         }
171         if (block && !ReadBlockFromDisk(*block, index, Params().GetConsensus())) {
172             block->SetNull();
173         }
174         return true;
175     }
guessVerificationProgress(const uint256 & block_hash)176     double guessVerificationProgress(const uint256& block_hash) override
177     {
178         LOCK(cs_main);
179         return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
180     }
requestMempoolTransactions(std::function<void (const CTransactionRef &)> fn)181     void requestMempoolTransactions(std::function<void(const CTransactionRef&)> fn) override
182     {
183         LOCK2(::cs_main, ::mempool.cs);
184         for (const CTxMemPoolEntry& entry : ::mempool.mapTx) {
185             fn(entry.GetSharedTx());
186         }
187     }
188 };
189 
190 } // namespace
191 
MakeChain()192 std::unique_ptr<Chain> MakeChain() { return MakeUnique<ChainImpl>(); }
193 
194 } // namespace interfaces
195