forked from cculianu/Fulcrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBTC.cpp
151 lines (136 loc) · 5.54 KB
/
BTC.cpp
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
//
// 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/>.
//
#include "BTC.h"
#include "Common.h"
#include "Util.h"
#include "bitcoin/crypto/endian.h"
#include "bitcoin/crypto/sha256.h"
#include "bitcoin/hash.h"
#include "bitcoin/pubkey.h"
#include "bitcoin/streams.h"
#include "bitcoin/utilstrencodings.h"
#include "bitcoin/version.h"
#include <algorithm>
#include <atomic>
#include <utility>
namespace bitcoin
{
inline void Endian_Check_In_namespace_bitcoin()
{
constexpr uint32_t magicWord = 0x01020304;
const uint8_t wordBytes[4] = {0x01, 0x02, 0x03, 0x04}; // represent above as big endian
const uint32_t bytesAsNum = *reinterpret_cast<const uint32_t *>(wordBytes);
if (magicWord != be32toh(bytesAsNum))
{
throw Exception(QString("Program compiled with incorrect WORDS_BIGENDIAN setting.\n\n")
+ "How to fix this:\n"
+ " 1. Adjust WORDS_BIGENDIAN in the qmake .pro file to match your architecture.\n"
+ " 2. Re-run qmake.\n"
+ " 3. Do a full clean recompile.\n\n");
}
}
extern bool TestBase58(bool silent, bool throws);
}
namespace BTC
{
void CheckBitcoinEndiannessAndOtherSanityChecks() {
bitcoin::Endian_Check_In_namespace_bitcoin();
if ( ! bitcoin::CSHA256::SelfTest() )
throw InternalError("sha256 self-test failed. Cannot proceed.");
Tests::Base58(true, true);
Debug() << "Using sha256 algorithm: " << bitcoin::SHA256AutoDetect();
}
namespace Tests {
bool Base58(bool silent, bool throws) { return bitcoin::TestBase58(silent, throws); }
}
/// specialization for CTransaction which works differently
template <> bitcoin::CTransaction Deserialize(const QByteArray &bytes, int pos)
{
bitcoin::GenericVectorReader<QByteArray> vr(bitcoin::SER_NETWORK, bitcoin::PROTOCOL_VERSION, bytes, pos);
return bitcoin::CTransaction(bitcoin::deserialize, vr);
}
QByteArray Hash(const QByteArray &b, bool once)
{
bitcoin::CHash256 h(once);
QByteArray ret(int(h.OUTPUT_SIZE), Qt::Initialization::Uninitialized);
h.Write(reinterpret_cast<const uint8_t *>(b.constData()), size_t(b.length()));
h.Finalize(reinterpret_cast<uint8_t *>(ret.data()));
return ret;
}
QByteArray HashRev(const QByteArray &b, bool once)
{
QByteArray ret = Hash(b, once);
std::reverse(std::begin(ret), std::end(ret));
return ret;
}
QByteArray Hash160(const QByteArray &b) {
bitcoin::CHash160 h;
QByteArray ret(int(h.OUTPUT_SIZE), Qt::Initialization::Uninitialized);
h.Write(reinterpret_cast<const uint8_t *>(b.constData()), size_t(b.length()));
h.Finalize(reinterpret_cast<uint8_t *>(ret.data()));
return ret;
}
// HeaderVerifier helper
bool HeaderVerifier::operator()(const QByteArray & header, QString *err)
{
const long height = prevHeight+1;
if (header.size() != BTC::GetBlockHeaderSize()) {
if (err) *err = QString("Header verification failed for header at height %1: wrong size").arg(height);
return false;
}
bitcoin::CBlockHeader curHdr = Deserialize<bitcoin::CBlockHeader>(header);
if (!checkInner(height, curHdr, err))
return false;
prevHeight = height;
prev = header;
if (err) err->clear();
return true;
}
bool HeaderVerifier::operator()(const bitcoin::CBlockHeader &curHdr, QString *err)
{
const long height = prevHeight+1;
QByteArray header = Serialize(curHdr);
if (header.size() != BTC::GetBlockHeaderSize()) {
if (err) *err = QString("Header verification failed for header at height %1: wrong size").arg(height);
return false;
}
if (!checkInner(height, curHdr, err))
return false;
prevHeight = height;
prev = header;
if (err) err->clear();
return true;
}
bool HeaderVerifier::checkInner(long height, const bitcoin::CBlockHeader &curHdr, QString *err)
{
if (curHdr.IsNull()) {
if (err) *err = QString("Header verification failed for header at height %1: failed to deserialize").arg(height);
return false;
}
if (!prev.isEmpty() && Hash(prev) != QByteArray::fromRawData(reinterpret_cast<const char *>(curHdr.hashPrevBlock.begin()), int(curHdr.hashPrevBlock.width())) ) {
if (err) *err = QString("Header %1 'hashPrevBlock' does not match the contents of the previous block").arg(height);
return false;
}
return true;
}
std::pair<int, QByteArray> HeaderVerifier::lastHeaderProcessed() const
{
return { int(prevHeight), prev };
}
} // end namespace BTC