forked from cculianu/Fulcrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBlockProc.h
179 lines (150 loc) · 8.8 KB
/
BlockProc.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//
// Fulcrum - A fast & nimble SPV Server for Bitcoin Cash
// Copyright (C) 2019-2020 Calin A. Culianu <calin.culianu@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program (see LICENSE.txt). If not, see
// <https://www.gnu.org/licenses/>.
//
#pragma once
#include "BTC.h"
#include "BlockProcTypes.h"
#include "Common.h"
#include "TXO.h"
#include "bitcoin/amount.h"
#include "bitcoin/block.h"
#include <QByteArray>
#include <cassert>
#include <cstdint>
#include <memory>
#include <optional>
#include <unordered_set>
#include <vector>
struct PreProcessedBlock;
using PreProcessedBlockPtr = std::shared_ptr<PreProcessedBlock>; ///< For clarity/convenience
/// Note all hashes below are in *reversed* order from bitcoind's internal memory order.
/// The reason for that is so that we have this PreProcessedBlock ready with the right format for putting into the db
/// for later serving up to EX clients.
struct PreProcessedBlock
{
BlockHeight height = 0; ///< the height (block number) of the block
size_t sizeBytes = 0; ///< the size of the original serialized block in bytes (not the size of this data structure which is significantly smaller)
size_t estimatedThisSizeBytes = 0; ///< the estimated size of this data structure -- may be off by a bit but is useful for rough estimation of memory costs of block processing
/// deserialized header as came in from bitcoind
bitcoin::CBlockHeader header;
struct TxInfo {
TxHash hash; ///< 32 byte txid. These txid's are *reversed* from bitcoind's internal memory order. (so as to be closer to the final hex encoded format).
IONum nInputs = 0, nOutputs = 0; ///< the number of inputs and outputs in the tx -- all tx's are guaranteed to have <=65535 inputs or outputs currently and for the foreseeable future. If that changes, fixme.
std::optional<unsigned> input0Index, output0Index; ///< if either of these have a value, they point into the `inputs` and `outputs` arrays below, respectively
};
/// The info for all the tx's in the block, in the order in which they appeared in the block.
std::vector<TxInfo> txInfos;
struct OutPt {
unsigned txIdx = 0; ///< this is an index into the `txInfos` vector declared above
IONum outN = 0; ///< this is an index into the tx's vout vector (*NOT* this class's `outputs`!) (again, tx's can't have more than 65535 inputs -- if that changes, fixme!)
bitcoin::Amount amount;
std::optional<unsigned> spentInInputIndex; ///< if has_value, the output was spent this block in input index (index into the `inputs` array)
};
struct InputPt {
unsigned txIdx = 0; ///< index into the `txInfos` vector above for the tx where this input appears
TxHash prevoutHash; ///< 32-byte prevoutHash. In *reversed* memory order (hex-encoding ready!) (May be a shallow copy of a byte array in `txInfos` if the prevout tx was in this block.). May be empty if coinbase
IONum prevoutN = 0; ///< the index in the prevout tx for this input (again, tx's can't have more than 65535 inputs -- if that changes, fixme!)
std::optional<unsigned> parentTxOutIdx; ///< if the input's prevout was in this block, the index into the `outputs` array declared in BlockProcBase, otherwise undefined.
};
std::vector<OutPt> outputs; ///< all the outpoints for *all* the tx's in this block, in the order they were encountered!
std::vector<InputPt> inputs; ///< all the inputs for *all* the tx's in this block, in the order they were encountered!
/// 'Value' type for the hashXAggregated map below. Contains 2 lists of output and input indices into the `outputs`
/// and `inputs` arrays present in concrete subclasses.
struct AggregatedOutsIns {
/// collection of all outputs in this block that are *TO* a particular HashX (data items are indices into the
/// `outputs`array above)
std::vector<unsigned> outs;
/// collection of all inputs in this block that are *FROM* a particular HashX (data items are indices into the
/// `inputs` arrays above). Note this will only include inputs that were from prevout tx's also in this block
/// for PreProcessedBlock instances before final processing (full resolution requires a utxo set).
std::vector<unsigned> ins;
/// Tx indices, always sorted. Initially it's just a list of txIdx into the txInfos array but gets transformed
/// down the block processing pipeline (in addBlock) to be a list of globally-mapped TxNums involving this
/// HashX.
std::vector<TxNum> txNumsInvolvingHashX;
};
/// Flat map ok here, robin_hood does move construction when moving objects around.
robin_hood::unordered_flat_map<HashX, AggregatedOutsIns, HashHasher> hashXAggregated;
/*
// If we decide to track OpReturn:
struct OpReturn {
unsigned outIdx; // index into the `outputs` vector declared above
bitcoin::CScript script;
};
std::vector<OpReturn> opreturns;
*/
// /OpReturn
// -- /End Data
unsigned nOpReturns = 0; ///< just keep a count of the number of opreturn outputs encountered in the block (used by sanity checkers)
// -- Methods:
// misc helpers --
/// returns the txHash given an index into the `outputs` array (or a null QByteArray if index is out of range).
const TxHash &txHashForOutputIdx(unsigned outputIdx) const {
if (outputIdx < outputs.size()) {
if (const auto txIdx = outputs[outputIdx].txIdx; txIdx < txInfos.size())
return txInfos[txIdx].hash;
}
return nullhash;
}
/// Given an index into the `outputs` array, return a bool as to whether the output is a coinbase tx
/// Note the coinbase INPUT would just be inputIdx==0 for any particular block. This function just tells you which
/// *outputs* are coinbase reward OUTPUTS.
bool isCoinbase(unsigned outputIdx) {
// since the outputs array is in blockchain order, just check the output is in the first tx. if it is, we
// know it's coinbase.
return outputIdx < outputs.size() && !txInfos.empty() && outputIdx < txInfos.front().nOutputs;
}
/// returns the input# as the input appeared in its tx, given a particular `inputs` array index
/// We do it this way rather than store this information in the InputPt struct to save on memory
std::optional<IONum> numForInputIdx(unsigned inputIdx) const {
std::optional<IONum> ret;
if (inputIdx < inputs.size()) {
if (const auto & opt = txInfos[inputs[inputIdx].txIdx].input0Index;
opt.has_value() && inputIdx >= opt.value()) {
const unsigned val = inputIdx - opt.value();
assert(val <= UINT16_MAX); // this should never happen -- all tx's are guaranteed to have <=65535 inputs or outputs currently and for the foreseeable future. If that changes, fixme.
ret.emplace( IONum(val) );
}
}
return ret;
}
/// returns the txHash given an index into the `inputs` array (or a null QByteArray if index is out of range).
const TxHash &txHashForInputIdx(unsigned inputIdx) const {
if (inputIdx < inputs.size()) {
if (const auto txIdx = inputs[inputIdx].txIdx; txIdx < txInfos.size())
return txInfos[txIdx].hash;
}
return nullhash;
}
// -- Methods:
// c'tors, etc... note this class is trivially copyable, move constructible, etc etc
PreProcessedBlock() = default;
PreProcessedBlock(BlockHeight bheight, size_t rawBlockSizeBytes, const bitcoin::CBlock &b) { fill(bheight, rawBlockSizeBytes, b); }
/// reset this to empty
inline void clear() { *this = PreProcessedBlock(); }
/// fill this block with data from bitcoin's CBlock
void fill(BlockHeight blockHeight, size_t rawSizeBytes, const bitcoin::CBlock &b);
/// convenience factory static method: given a block, return a shard_ptr instance of this struct
static PreProcessedBlockPtr makeShared(unsigned height, size_t sizeBytes, const bitcoin::CBlock &block);
/// debug string
QString toDebugString() const;
/// This is not totally complete until this class has consulted the UTXO set to fill in all inputs.
std::vector<std::unordered_set<HashX, HashHasher>> hashXsByTx() const;
protected:
static const TxHash nullhash;
};