forked from cculianu/Fulcrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTXO_Compact.h
136 lines (129 loc) · 6.24 KB
/
TXO_Compact.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
//
// 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 "BlockProcTypes.h"
#include <QByteArray>
#include <QString>
#include <cstdint>
#include <cstring> // for std::memcpy
#include <functional> // for std::hash
// -------------------------------------------------------------------------------------------------------------------
// ----- Some storage helper classes below.. (safe to ignore in rest of codebase outside of Storage.h / Storage.cpp)
// -------------------------------------------------------------------------------------------------------------------
#ifdef __GNUC__
#pragma pack(push, 1)
#endif
/// Stogage subsystem "compact txo"
/// This is used internally by the "Storage" subsystem for storing TxNum <-> txhash associations on disk
struct CompactTXO {
static constexpr std::uint64_t initval = ~0ULL; ///< indicates !isValid()
// pack paranoia -- not strictly needed since this packs anyway the way we want on gcc and/or clang.
union {
struct {
/// tx index in the global tx table. First tx in blockchain is txNum 0, and so on (mapping of unique number to a txid hash, stored in db).
/// Note this is really a 48 bit value covering the range [0, 17592186044415]
std::uint64_t txNum : 48;
/// The the 'N' (output index) for the tx itself as per bitcoin tx format
std::uint16_t n;
} prevout;
std::uint64_t asU64 = initval;
} u;
CompactTXO() = default;
CompactTXO(TxNum txNum, IONum n) { u.prevout.txNum = txNum; u.prevout.n = n; }
CompactTXO(const CompactTXO & o) { *this = o; }
CompactTXO & operator=(const CompactTXO & o) noexcept { std::memcpy(&u.prevout, &o.u.prevout, sizeof(u.prevout)); return *this; }
/// for most container types
bool operator==(const CompactTXO &o) const noexcept { return u.prevout.txNum == o.u.prevout.txNum && u.prevout.n == o.u.prevout.n; }
/// for ordered sets
bool operator<(const CompactTXO &o) const noexcept { return u.prevout.txNum == o.u.prevout.txNum ? u.prevout.n < o.u.prevout.n : u.prevout.txNum < o.u.prevout.txNum; }
// convenience
TxNum txNum() const noexcept { return TxNum(u.prevout.txNum); }
// convenience
IONum N() const noexcept { return IONum(u.prevout.n); }
bool isValid() const { return u.asU64 != initval; }
static constexpr size_t serSize() noexcept { return 8; }
QString toString() const { return isValid() ? QString("%1:%2").arg(txNum()).arg(N()) : "<compact_txo_invalid>"; }
/// Low-level serialization to a byte buffer in place. Note that bufsz must be >= serSize().
/// Number of bytes written is returned, or 0 if bufsz to small.
size_t toBytesInPlace(void *buf, size_t bufsz) const {
if (bufsz >= serSize()) {
uint8_t * cur = reinterpret_cast<uint8_t *>(buf);
txNumToCompactBytes(cur, u.prevout.txNum);
cur[6] = (u.prevout.n >> 0) & 0xff;
cur[7] = (u.prevout.n >> 8) & 0xff;
return serSize();
}
return 0;
}
QByteArray toBytes() const {
// the below is excessively wordy but it forces 8 byte little-endian style serialization
QByteArray ret(serSize(), Qt::Uninitialized);
toBytesInPlace(ret.data(), size_t(ret.size())); // this should never fail
return ret;
}
static CompactTXO fromBytes(const void *buf, size_t bufsz) {
return fromBytes(QByteArray::fromRawData(reinterpret_cast<const char *>(buf),int(std::min(bufsz, serSize()))));
}
/// passed-in QByteArray must be exactly serSize() bytes else nothing is converted
static CompactTXO fromBytes(const QByteArray &b) {
// the below is excessively wordy but it forces 8 byte little-endian style deserialization
CompactTXO ret;
if (b.size() == serSize()) {
const uint8_t * cur = reinterpret_cast<const uint8_t *>(b.data());
ret.u.prevout.txNum = txNumFromCompactBytes(cur);
ret.u.prevout.n = IONum(cur[6]) | IONum(IONum(cur[7]) << 8);
}
return ret;
}
/// Converts: TxNum (8 bytes) <- from a 6-byte buffer. Uses little-endian ordering.
static inline TxNum txNumFromCompactBytes(const uint8_t bytes[6])
{
return (TxNum(bytes[0])<<0)
| (TxNum(bytes[1])<<8)
| (TxNum(bytes[2])<<16)
| (TxNum(bytes[3])<<24)
| (TxNum(bytes[4])<<32)
| (TxNum(bytes[5])<<40);
}
/// Converts: TxNum (8 bytes) -> into a 6-byte buffer. Uses little-endian ordering.
static inline void txNumToCompactBytes(uint8_t bytes[6], TxNum num)
{
bytes[0] = (num >> 0) & 0xff;
bytes[1] = (num >> 8) & 0xff;
bytes[2] = (num >> 16) & 0xff;
bytes[3] = (num >> 24) & 0xff;
bytes[4] = (num >> 32) & 0xff;
bytes[5] = (num >> 48) & 0xff;
}
static constexpr size_t compactTxNumSize() { return 6; }
};
#ifdef __GNUC__
#pragma pack(pop)
#endif
namespace std {
/// specialization of std::hash to be able to add struct CompactTXO to any unordered_set or unordered_map
template<> struct hash<CompactTXO> {
size_t operator()(const CompactTXO &txo) const noexcept {
if constexpr (sizeof(txo.u.asU64) <= sizeof(size_t) && sizeof(txo.u.prevout) >= 8
&& sizeof(txo.u.prevout) == sizeof(txo.u.asU64))
return txo.u.asU64; // just return the packed txNum:n value as this is guaranteed to be unique
return (txo.u.prevout.txNum<<16) | size_t(txo.u.prevout.n&0xffff);
}
};
} // namespace std