forked from cculianu/Fulcrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTXO.h
132 lines (114 loc) · 5.2 KB
/
TXO.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
//
// 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 "TXO_Compact.h"
#include "robin_hood/robin_hood.h"
#include <QString>
#include <cstdint>
#include <cstring> // for std::memcpy
#include <functional> // for std::hash
#include <optional>
/// WIP
struct TXO {
TxHash prevoutHash;
IONum prevoutN = 0;
bool isValid() const { return prevoutHash.length() == HashLen; }
QString toString() const { return isValid() ? QString("%1:%2").arg(QString(prevoutHash.toHex())).arg(prevoutN) : "<txo_invalid>"; }
bool operator==(const TXO &o) const noexcept { return prevoutHash == o.prevoutHash && prevoutN == o.prevoutN; }
bool operator<(const TXO &o) const noexcept { return prevoutHash < o.prevoutHash && prevoutN < o.prevoutN; }
// serialization/deserialization
QByteArray toBytes() const noexcept {
QByteArray ret;
if (!isValid()) return ret;
const int hlen = prevoutHash.length();
ret.resize(int(serSize()));
std::memcpy(ret.data(), prevoutHash.data(), size_t(hlen));
std::memcpy(ret.data() + hlen, &prevoutN, sizeof(prevoutN));
return ret;
}
static TXO fromBytes(const QByteArray &ba) noexcept {
TXO ret;
if (ba.length() != int(serSize())) return ret;
ret.prevoutHash = QByteArray(ba.data(), HashLen);
ret.prevoutN = *reinterpret_cast<const IONum *>(ba.data()+HashLen);
return ret;
}
static constexpr size_t serSize() noexcept { return HashLen + sizeof(IONum); }
};
namespace std {
/// specialization of std::hash to be able to add struct TXO to any unordered_set or unordered_map as a key
template<> struct hash<TXO> {
size_t operator()(const TXO &txo) const noexcept {
if (txo.prevoutHash.length() >= int(sizeof(size_t)))
return *reinterpret_cast<const size_t *>(txo.prevoutHash.constData()) + size_t(txo.prevoutN);
using h1 = std::hash<uint>;
using h2 = std::hash<IONum>;
return h1()(qHash(txo.prevoutHash)) + h2()(txo.prevoutN);
}
};
}
/// WIP -- Spend info for a txo.
struct TXOInfo {
bitcoin::Amount amount;
HashX hashX; ///< the scripthash this output is sent to. Note in most cases this can be compactified to be a shallow-copy of existing data (such that dupes point to the same underlying data in eg UTXOSet).
std::optional<unsigned> confirmedHeight; ///< if unset, is mempool tx
TxNum txNum = 0; ///< the globally mapped txNum (one for each TxHash). This is used to be able to delete the CompactTXO from the hashX's scripthash_unspent table
bool isValid() const { return amount / bitcoin::Amount::satoshi() >= 0 && hashX.length() == HashLen; }
/// for debug, etc
bool operator==(const TXOInfo &o) const
{ return amount == o.amount && hashX == o.hashX && confirmedHeight == o.confirmedHeight && txNum == o.txNum; }
QByteArray toBytes() const noexcept {
QByteArray ret;
if (!isValid()) return ret;
const auto amt_sats = amount / bitcoin::Amount::satoshi();
const int cheight = confirmedHeight.has_value() ? int(confirmedHeight.value()) : -1;
ret.resize(int(serSize()));
char *cur = ret.data();
std::memcpy(cur, &amt_sats, sizeof(amt_sats));
cur += sizeof(amt_sats);
std::memcpy(cur, &cheight, sizeof(cheight));
cur += sizeof(cheight);
CompactTXO::txNumToCompactBytes(reinterpret_cast<uint8_t *>(cur), txNum);
cur += CompactTXO::compactTxNumSize();
std::memcpy(cur, hashX.constData(), size_t(hashX.length()));
return ret;
}
static TXOInfo fromBytes(const QByteArray &ba) {
TXOInfo ret;
if (size_t(ba.length()) != serSize()) {
return ret;
}
int64_t amt;
int cheight;
const char *cur = ba.constData();
std::memcpy(&amt, cur, sizeof(amt));
cur += sizeof(amt);
std::memcpy(&cheight, cur, sizeof(cheight));
cur += sizeof(cheight);
ret.txNum = CompactTXO::txNumFromCompactBytes(reinterpret_cast<const uint8_t *>(cur));
cur += CompactTXO::compactTxNumSize();
ret.hashX = QByteArray(cur, HashLen);
ret.amount = amt * bitcoin::Amount::satoshi();
if (cheight > -1)
ret.confirmedHeight.emplace(unsigned(cheight));
return ret;
}
static constexpr size_t serSize() noexcept { return sizeof(int64_t) + sizeof(int) + CompactTXO::compactTxNumSize() + HashLen; }
};