forked from cculianu/Fulcrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBTC_unused.cpp
772 lines (731 loc) · 34.1 KB
/
BTC_unused.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
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
//
// Fulcrum - A fast & nimble SPV Server for Electron 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 "BTC_unused.h"
#include "Common.h"
#include "Util.h"
#include "bitcoin/base58.h"
#include "bitcoin/cashaddrenc.h"
#include "bitcoin/crypto/sha256.h"
#include "bitcoin/interpreter.h"
#include "bitcoin/script.h"
#include "bitcoin/script_error.h"
#include "bitcoin/utilstrencodings.h"
#include <iostream>
#ifdef __clang__
#pragma clang diagnostic push
// we get warnings using bitcoin templates but they compile and work anyway.
#pragma clang diagnostic ignored "-Wundefined-func-template"
#endif
namespace BTC {
namespace InitData {
// setup the global secp verify context at app startup.
extern bitcoin::ECCVerifyHandle myVerifyHandle;
bitcoin::ECCVerifyHandle myVerifyHandle; // this singleton object allocates a secp handle. see bitcoin/pubkey.h
}
// Map of Net -> [Map of VerByte -> Kind]
static QMap<Net, QMap<quint8, Address::Kind> > netVerByteKindMap = {
{ MainNet, { {0, Address::P2PKH }, {5, Address::P2SH} } },
{ TestNet, { {111, Address::P2PKH },{196, Address::P2SH} } },
};
/// -- Address --
Address::Address(const QString &legacyOrCash)
{
*this = Address::fromString(legacyOrCash);
}
/*static*/
Address Address::fromString(const QString &legacyOrCash)
{
static const auto DecodeCash = [] (Address & a, const QString &s) -> bool {
const auto & ss = s.toStdString();
auto content = bitcoin::DecodeCashAddrContent(ss, bitcoin::MainNetChainParams.CashAddrPrefix());
bool isTestnet = false;
if (content.hash.empty()) {
// try testnet
content = bitcoin::DecodeCashAddrContent(ss, bitcoin::TestNetChainParams.CashAddrPrefix());
isTestnet = !content.hash.empty();
}
if (!content.hash.empty()) {
const auto whichNet = isTestnet ? TestNet : MainNet;
auto & map = netVerByteKindMap[whichNet];
if (content.type == bitcoin::PUBKEY_TYPE && !map.isEmpty()) {
a.verByte = map.firstKey();
} else if (content.type == bitcoin::SCRIPT_TYPE && !map.isEmpty()) {
a.verByte = map.lastKey();
} else
// Defensive programming.. we should never reach this branch.
throw Exception("unknown type or other missing data on cash addr decode attempt");
a.h160.clear();
a.h160.insert(0, reinterpret_cast<const char *>(content.hash.data()), int(content.hash.size()));
a.net = whichNet;
return true;
}
return false;
};
Address a;
ByteArray dec;
try {
if (!bitcoin::DecodeBase58Check(legacyOrCash.toUtf8().constData(), dec)) {
if (!DecodeCash(a, legacyOrCash))
Debug() << __FUNCTION__ << ": got bad address " << legacyOrCash;
return a; // a is either valid or invalid here, depending on return value of DecodeCash in line above.
}
} catch (const std::runtime_error &e) {
Error() << "Internal error decoding address " << legacyOrCash << ": " << e.what();
return a;
}
a.verByte = dec[0];
a.h160.resize(int(dec.size()-1));
memcpy(a.h160.data(), &dec[1], dec.size()-1);
a.net = BTC::Invalid;
// figure out the net based on the verbyte, if the verbyte is in our map
for (auto it = netVerByteKindMap.begin(); it != netVerByteKindMap.end(); ++it) {
if (it.value().contains(a.verByte))
a.net = it.key();
}
return a;
}
/* static */
Address Address::fromPubKey(const Byte *pbegin, const Byte *pend, Net net)
{
Address ret;
const auto hash160 = bitcoin::Hash160(pbegin, pend);
ret.h160 = QByteArray(reinterpret_cast<const char *>(hash160.begin()), int(hash160.size()));
ret.verByte = 0;
ret.net = net;
if (auto map = netVerByteKindMap.value(net); !map.isEmpty())
ret.verByte = map.begin().key(); // P2PKH verbyte
return ret;
}
bool Address::isValid() const
{
return kind() != Invalid;
}
Address::Kind Address::kind() const
{
// NB: all the isValid() functions eventually end up here.
if (h160.length() == 20) {
auto it = netVerByteKindMap.find(net);
if (it != netVerByteKindMap.end()) {
auto it2 = it.value().find(verByte);
if (it2 != it.value().end()) {
return it2.value();
}
}
}
return Invalid;
}
ByteArray Address::toScript() const
{
ByteArray script;
using bitcoin::OP_DUP, bitcoin::OP_HASH160, bitcoin::OP_EQUALVERIFY, bitcoin::OP_CHECKSIG, bitcoin::OP_EQUAL;
if (kind() == P2PKH) { // kind() checks for validity
script << OP_DUP << OP_HASH160 << Byte(h160.length()) << h160 << OP_EQUALVERIFY << OP_CHECKSIG;
} else if (kind() == P2SH) {
script << OP_HASH160 << Byte(h160.length()) << h160 << OP_EQUAL;
}
return script;
}
bitcoin::CScript Address::toCScript() const
{
auto ba = toScript();
return bitcoin::CScript(ba.begin(), ba.end());
}
ByteArray Address::toScriptHash() const
{
ByteArray script(toScript()), ret;
if (!script.isEmpty()) {
auto hash = bitcoin::HashOnce(script.begin(), script.end());
ret.insert(ret.end(), hash.begin(), hash.end());
}
return ret;
}
QByteArray Address::toScriptHashQ() const
{
ByteArray script(toScript());
QByteArray ret;
if (!script.isEmpty()) {
auto hash = bitcoin::HashOnce(script.begin(), script.end());
ret.insert(0, reinterpret_cast<char *>(hash.begin()), int(hash.end()-hash.begin()));
}
return ret;
}
/// returns the ElectrumX 'scripthash_hex'
QByteArray Address::toHashX() const
{
if (!cachedHashX.isEmpty())
return cachedHashX;
QByteArray ret;
auto script = toScript();
if (!script.isEmpty()) {
// Note as a performance tweak here we don't call toScriptHash() as that would do extra copying.
// Instead, we just reproduce some of its work here.
bitcoin::uint256 hash = bitcoin::HashOnce(script.begin(), script.end());
auto str = hash.GetHex(); /// this is reversed hex
ret = str.c_str();
cachedHashX = ret;
}
return ret;
}
/// if isValid, returns the legacy address string, base58 encoded
QString Address::toString() const {
QString ret;
if (isValid()) {
ByteArray vch = ByteArray({verByte}) + h160;
auto str = bitcoin::EncodeBase58Check(vch);
ret = QString::fromUtf8(str.c_str());
}
return ret;
}
/*static*/
bool Address::isValid(const QString &legacyOrCashAddress, Net net)
{
Address a(legacyOrCashAddress);
return a.isValid() && a.net == net;
}
/*static*/
bool Address::test()
{
//const char *badAddress = "1C3SoftYBC2bbDzCadZxDrfbnobEXLBLQQ";
const char *anAddress = "1C3SoftYBC2bbDzCadZxDrfbnobEXLBLQZ";
Address a = anAddress, b; //bad(badAddress);
b = a;
Address c(a);
std::cout << "a < b? " << int(a < b) << std::endl;
std::cout << "a <= b? " << int(a <= b) << std::endl;
std::cout << "a == b? " << int(a == b) << std::endl;
c = b;
// NOTE: the below tests are unsafe because they access charData() which may not have a nul byte at the end.
// If this crashes, then modify the code below to read into QStrings or something like that.
// On my platform it just happened to work and I was testing things quickly so I didn't bother to
// do the below the correct way.
std::cout << "Decoded -> VerByte: " << int(a.verByte) << " Hash160 (hex): " << a.h160.toHex().constData() << std::endl;
ByteArray v = { 'a', ' ', 'b', 'c', 0 };
ByteArray v2 = "this is a test";
auto vcat = ByteArray({'a','b','c',' '}) + v2;
std::vector<Byte> v3(v2); // support construction from ByteArray to vector
ByteArray v4(v3); // support construction from vector to ByteArray
std::cout << "Init list test: " << v.charData() << " .length() = " << v.length() << std::endl;
ByteArray inl("12345");
std::cout << "Inline string: " << inl.charData() << " .length() = " << inl.length() << std::endl;
std::cout << "Init string test: " << v2.charData() << " .length() = " << v2.length() << std::endl;
std::cout << "Chained c'tor string test: " << v4.charData() << std::endl;
std::cout << "Concat test: " << (vcat + ByteArray({0})).charData() << std::endl;
std::cout << "Concat test 2: " << ((vcat+"..more stuff")+ByteArray({'z','z','z',0})).charData() << std::endl;
std::cout << "v < v2 : " << int(v < v2) << std::endl;
std::cout << "IsValid: " << a.isValid() << " kind: " << a.kind() << std::endl;
std::cout << "Script Hex of: " << a.toString().toUtf8().constData() << " = " << a.toScript().toQHex().constData() << std::endl;
std::cout << "Script Hash (Hex) of: " << a.toString().toUtf8().constData() << " = " << a.toScriptHash().toQHex().constData() << std::endl;
std::cout << "Script Hash (Qt, Hex) of: " << a.toString().toUtf8().constData() << " = " << a.toScriptHashQ().toHex().constData() << std::endl;
std::cout << "HashX of " << a.toString().toUtf8().constData() << " = " << a.toHashX().constData() << std::endl;
c = a;
std::cout << "HashX again " << c.toString().toUtf8().constData() << " = " << c.toHashX().constData() << std::endl;
std::cout << "c==a : " << int(c==a) << std::endl;
std::cout << "c==b : " << int(c==b) << " (cached?,cached?): (" << int(!c.cachedHashX.isEmpty()) << "," << int(!b.cachedHashX.isEmpty()) << ")" << std::endl;
//std::cout << "Testnet: " << a.toString().toUtf8().constData() << std::endl;
return a.isValid() && a.toString() == anAddress && a == b;
}
// -- ByteArray --
ByteArray::ByteArray() : std::vector<Byte>() {}
ByteArray::ByteArray(const std::vector<Byte> &b) : std::vector<Byte>(b) {}
ByteArray::ByteArray(std::vector<Byte> &&o) : std::vector<Byte>(std::move(o)) {}
ByteArray::ByteArray(const std::initializer_list<Byte> &il) : std::vector<Byte>(il) {}
ByteArray::ByteArray(const QByteArray &a) { (*this) = a; } // leverage operator=
ByteArray::ByteArray(const QString &s) { (*this) = s; } // leverage operator=
static Byte emptyBytes[sizeof(long)] = {0}; ///< C++ init would have been zero anyway. We do it like this to illustrate the point to the casual observer.
/* static */
ByteArray ByteArray::fromHex(const QString &s)
{
return ByteArray(bitcoin::ParseHex(s.toUtf8().constData()));
}
Byte *ByteArray::data()
{
if (!empty()) return &(*this)[0];
return emptyBytes;
}
const Byte* ByteArray::constData() const
{
if (!empty()) return &(*this)[0];
return emptyBytes;
}
ByteArray ByteArray::operator+(const std::vector<Byte> &b) const
{
ByteArray ret(*this);
ret += b;
return ret;
}
ByteArray ByteArray::operator+(const QByteArray & o) const
{
ByteArray ret(*this);
ret += o;
return ret;
}
ByteArray ByteArray::operator+(const QString &s) const
{
ByteArray ret(*this);
ret += s;
return ret;
}
ByteArray ByteArray::operator+(const std::initializer_list<Byte> &il) const
{
ByteArray ret(*this);
ret += il;
return ret;
}
ByteArray & ByteArray::operator+=(const std::vector<Byte> & b)
{
if (!b.empty())
insert(end(), b.begin(), b.end());
return *this;
}
ByteArray & ByteArray::operator+=(const QByteArray &b)
{
if (!b.isEmpty())
insert(end(), b.begin(), b.end());
return *this;
}
ByteArray & ByteArray::operator+=(const QString &s)
{
return (*this) += s.toUtf8();
}
ByteArray & ByteArray::operator+=(const std::initializer_list<Byte> &il)
{
return (*this) += ByteArray(il);
}
ByteArray & ByteArray::operator=(const std::vector<Byte> &a)
{
clear();
return (*this) += a;
}
ByteArray & ByteArray::operator=(const QByteArray &a)
{
clear();
return (*this) += a;
}
ByteArray & ByteArray::operator=(const QString &a)
{
clear();
return (*this) += a;
}
ByteArray & ByteArray::operator=(const std::initializer_list<Byte> &il)
{
clear();
return *this += il;
}
ByteArray::operator QByteArray() const
{
QByteArray ret;
if (!empty())
ret.append(reinterpret_cast<const char *>(constData()), length());
return ret;
}
///< append a Byte to this array
ByteArray & ByteArray::operator<<(Byte b)
{
insert(end(), b);
return *this;
}
ByteArray ByteArray::toHex() const
{
return ByteArray(toQHex());
}
QByteArray ByteArray::toQHex() const
{
QByteArray qba = *this;
return qba.toHex();
}
/// UTXO
QString UTXO::toString() const {
QString ret;
if (isValid()) {
ret = QString("%1:%2").arg(_txid).arg(_n);
}
return ret;
}
bitcoin::COutPoint UTXO::toCOutPoint() const
{
return bitcoin::COutPoint(toString());
}
/// will only accept if the hash is valid hex, otherwise will leave this class in "Invalid" state
UTXO & UTXO::setCheck(const QString &prevoutHash, quint32 n)
{
bitcoin::uint256 h;
QString trimd(prevoutHash.trimmed());
h.SetHex(trimd.toUtf8());
if (h.GetHex() == trimd.toStdString()) {
_txid = trimd;
_n = n;
} else
clear();
return *this;
}
UTXO & UTXO::setCheck(const QString &prevoutN)
{
auto l = prevoutN.split(":");
bool ok;
unsigned N = 0;
if (l.length() == 2 && ((N = l.back().toUInt(&ok)) || ok)) {
setCheck(l.front(), N);
} else
clear();
return *this;
}
/* static */
void UTXO::test()
{
UTXO u("0a4bd:13"), u2;
u2 = u;
qInfo("u isValid? %d str=%s", int(u.isValid()), Q2C(u.toString()));
u = "f6b0fc46aa9abb446b3817f9f5898f45233b274692d110203e2fe38c2f9e9ee3:56";
qInfo("u isValid? %d str=%s", int(u.isValid()), Q2C(u.toString()));
auto outpt = u.toCOutPoint();
qInfo("U hex:%s N:%u", outpt.GetTxId().ToString().c_str(), outpt.GetN());
u2 = u;
qInfo("u == u2 ? %d", int(u == u2));
u2.setCheck(u.txid(), u.n()+4);
qInfo("u2: %s ... u == u2 ? %d u < u2 ? %d u <= u2 ? %d", Q2C(u2.toString()), int(u == u2), int(u < u2), int(u <= u2));
qInfo("u: %s ... u == u2 ? %d u2 < u ? %d", Q2C(u.toString()), int(u == u2), int(u2 < u));
}
int64_t MakeUnsignedTransaction(bitcoin::CMutableTransaction & tx,
const QList<UTXO> & inputs, const QList<QPair<Address, int64_t> > & outputs,
quint32 nLockTime, int nVersion, quint32 nSequence)
{
int64_t ret = 0;
static const auto clearTx = [nVersion](bitcoin::CMutableTransaction & tx, int resrv_in = 0, int resrv_out = 0) {
tx.vin.clear();
tx.vout.clear();
tx.nVersion = nVersion > 0 ? nVersion : bitcoin::CTransaction::CURRENT_VERSION;
tx.nLockTime = 0;
if (resrv_in >= 0) tx.vin.reserve(size_t(resrv_in));
if (resrv_out >= 0) tx.vout.reserve(size_t(resrv_out));
};
clearTx(tx, inputs.size(), outputs.size());
tx.nLockTime = nLockTime;
try {
int n = 0;
for (const auto & utxo : inputs) {
tx.vin.emplace_back(bitcoin::CTxIn(utxo.toCOutPoint()));
tx.vin.back().nSequence = nSequence;
if (!utxo.isValid())
throw Exception(QString("Bad utxo specified in tx for input: %1").arg(n));
++n;
}
if (!n) throw Exception("No inputs specified for tx");
n = 0;
for (const auto & adrAmt : outputs) {
auto & addr = adrAmt.first;
const auto amt = adrAmt.second*bitcoin::SATOSHI;
constexpr auto DUST_THRESHOLD = int64_t(546)*bitcoin::SATOSHI;
if (!addr.isValid())
throw Exception(QString("Bad address specified in tx for output %1").arg(n));
if (amt < DUST_THRESHOLD)
throw Exception(QString("Bad amount specified in tx for output %1: %2 < %3").arg(n).arg(amt.ToString().c_str()).arg(DUST_THRESHOLD.ToString().c_str()));
ret += adrAmt.second;
tx.vout.emplace_back(bitcoin::CTxOut(amt, addr.toCScript()));
++n;
}
if (!n) throw Exception("No outputs specified for tx");
} catch (const std::exception & e) {
Warning() << e.what();
clearTx(tx);
ret = 0;
}
Q_ASSERT(ret >= 0);
return ret;
}
bool VerifyTxSignature(const bitcoin::CMutableTransaction &tx,
const ByteArray & sigData, const ByteArray & pubKeyData,
uint nInput, int64_t inputValSatoshis,
QString *errIn, bitcoin::CScript *scriptSig_out)
{
QString dummy, &errStr = (errIn ? *errIn : dummy);
bitcoin::CScript scriptSig;
scriptSig << sigData << pubKeyData;
bitcoin::ScriptError err;
const auto & scriptPubKey = Address::fromPubKey(pubKeyData).toCScript();
bool ret = bitcoin::VerifyScript
(
scriptSig,
scriptPubKey,
bitcoin::SCRIPT_ENABLE_SIGHASH_FORKID
| bitcoin::SCRIPT_VERIFY_STRICTENC
| bitcoin::SCRIPT_VERIFY_LOW_S
| bitcoin::SCRIPT_VERIFY_DERSIG,
bitcoin::MutableTransactionSignatureChecker(&tx, nInput, inputValSatoshis*bitcoin::SATOSHI),
&err
);
errStr = bitcoin::ScriptErrorString(err);
if (ret && scriptSig_out)
// caller wants the valid script, so swap the buffers to provide it
scriptSig_out->swap(scriptSig);
return ret;
}
namespace Tests {
void SigCheck()
{
using namespace bitcoin;
static const auto BuildCreditingTransaction =
[] (const CScript &scriptPubKey, const Amount nValue) -> CMutableTransaction {
CMutableTransaction txCredit;
txCredit.nVersion = 1;
txCredit.nLockTime = 0;
txCredit.vin.resize(1);
txCredit.vout.resize(1);
txCredit.vin[0].prevout = COutPoint();
txCredit.vin[0].scriptSig = CScript() << CScriptNum(0) << CScriptNum(0);
txCredit.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
txCredit.vout[0].scriptPubKey = scriptPubKey;
txCredit.vout[0].nValue = nValue;
return txCredit;
};
static const auto BuildSpendingTransaction =
[](const CScript &scriptSig, const CMutableTransaction &txCredit) -> CMutableTransaction {
CMutableTransaction txSpend;
txSpend.nVersion = 1;
txSpend.nLockTime = 0;
txSpend.vin.resize(1);
txSpend.vout.resize(1);
txSpend.vin[0].prevout = COutPoint(txCredit.GetId(), 0);
txSpend.vin[0].scriptSig = scriptSig;
txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
txSpend.vout[0].scriptPubKey = CScript();
txSpend.vout[0].nValue = txCredit.vout[0].nValue;
return txSpend;
};
static const auto VerifyTx =
[](const QString & pubKeyHex, const QString &sigHex, int64_t nValue=0, uint32_t flags=0,
uint32_t nLockTime=0, uint32_t sequence=CTxIn::SEQUENCE_FINAL, const QString & prevOutOverride = "",
const QString & outAddr = "", int64_t spendVal = -1)
{
const auto pubKeyData = ByteArray::fromHex(pubKeyHex);
const auto sigData = ByteArray::fromHex(sigHex);
Address addr = Address::fromPubKey(pubKeyData);
CScript scriptSig;
scriptSig << sigData << pubKeyData;
auto scriptSigHex = QByteArray(reinterpret_cast<char *>(scriptSig.data()), int(scriptSig.size())).toHex();
Log() << "Address is: " << addr.toString() << " pubKey: " << pubKeyData.toHex() << " scriptPubKey: " << addr.toScriptHash().toHexStr() << " hash160: " << addr.hash160().toHex() << " scriptSig: " << scriptSigHex;
ScriptError err;
auto scriptPubKey = addr.toCScript();
CMutableTransaction txCredit =
BuildCreditingTransaction(scriptPubKey, nValue*SATOSHI);
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit);
CMutableTransaction tx2 = tx;
tx.nLockTime = nLockTime;
tx.vin[0].nSequence = sequence;
if (!prevOutOverride.isEmpty())
tx.vin[0].prevout.SetQString(prevOutOverride);
if (!outAddr.isEmpty())
tx.vout[0].scriptPubKey = Address(outAddr).toCScript();
if (spendVal > 0)
tx.vout[0].nValue = spendVal*SATOSHI;
bool ret = VerifyScript
(
scriptSig,
scriptPubKey,
flags,
MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue),
&err
);
Log() << "Verify: " << int(ret) << " err: " << ScriptErrorString(err);
};
VerifyTx("038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508",
"304402201e0ec3c6c263f34049c93e0bc646d7287ca2cc6571d658e4e7269daebc96ef35022009841f101e6dcaba8993d0259e5732a871e253be807556bf5618bf0bc3e84af001");
VerifyTx("0277b926d8fd088be302ed207d7d35ca6e7b78005c415bdf9873b45337939704cd",
"30440220757c81c9aea06f19ce8bcf3ca088e28f0659273e8deb6dabc8e7fdeb7d235f6c0220688fa0ba75debf36b1a45a2d10ee18c9f546eb1aa6a8e08d1a96d9c08b95a21c41",
1111, SCRIPT_ENABLE_SIGHASH_FORKID|SCRIPT_VERIFY_STRICTENC|SCRIPT_VERIFY_LOW_S, 577472, 4294967294,
"4058a690de126e5b696dba53c9e63d0344adf5487ba1e0124322ba2735c74bd1:0",
"1Ca1inCimwRhhcpFX84TPRrPQSryTgKW6N", 919);
CMutableTransaction tx3;
MakeUnsignedTransaction(
tx3,
{ UTXO("4058a690de126e5b696dba53c9e63d0344adf5487ba1e0124322ba2735c74bd1:0") },
{ { Address("1Ca1inCimwRhhcpFX84TPRrPQSryTgKW6N"), 919} },
577472
);
QString errStr;
auto b = VerifyTxSignature(tx3,
ByteArray::fromHex("30440220757c81c9aea06f19ce8bcf3ca088e28f0659273e8deb6dabc8e7fdeb7d235f6c0220688fa0ba75debf36b1a45a2d10ee18c9f546eb1aa6a8e08d1a96d9c08b95a21c41"),
ByteArray::fromHex("0277b926d8fd088be302ed207d7d35ca6e7b78005c415bdf9873b45337939704cd"),
0, 1111, &errStr);
Log() << "VerifyTxSignature: " << int(b) << " errStr: " << errStr;
}
void CashAddr() {
using namespace bitcoin;
auto content = DecodeCashAddrContent("bitcoincash:qphaxewltpcd5pcwr074tmrn7ged4h9ayuxp49h7nh","bitcoincash");
Log() << "Decoded type: " << content.type << ", bytes (hex): " << (content.hash.empty() ? "" : HexStr(content.hash));
content = DecodeCashAddrContent("qphaxewltpcd5pcwr074tmrn7ged4h9ayuxp49h7nh","bitcoincash");
Log() << "Decoded type: " << content.type << ", bytes (hex): " << (content.hash.empty() ? "" : HexStr(content.hash));
content = DecodeCashAddrContent("qphaxewltpcd5pcwr074tmrn7ged4h9ayuxp49h7nh","bchtest");
Log() << "Decoded type: " << content.type << ", bytes (hex): " << (content.hash.empty() ? "" : HexStr(content.hash));
Address a("qphaxewltpcd5pcwr074tmrn7ged4h9ayuxp49h7nh");
Log() << "Address as legacy: " << a.toString();
}
void TestBlock()
{
/* Bitcoin Block # 100000 with 4 txns in it. Test deserialization of blocks. Works! We commented this
* out to reduct binary size.. */
/*
QByteArray blockHex (
"0100000050120119172a610421a6c3011dd330d9df07b63616c2cc1f1cd00200000000"
"006657a9252aacd5c0b2940996ecff952228c3067cc38d4885efb5a4ac4247e9f33722"
"1b4d4c86041b0f2b571004010000000100000000000000000000000000000000000000"
"00000000000000000000000000ffffffff08044c86041b020602ffffffff0100f2052a"
"010000004341041b0e8c2567c12536aa13357b79a073dc4444acb83c4ec7a0e2f99dd7"
"457516c5817242da796924ca4e99947d087fedf9ce467cb9f7c6287078f801df276fdf"
"84ac000000000100000001032e38e9c0a84c6046d687d10556dcacc41d275ec55fc007"
"79ac88fdf357a187000000008c493046022100c352d3dd993a981beba4a63ad15c2092"
"75ca9470abfcd57da93b58e4eb5dce82022100840792bc1f456062819f15d33ee7055c"
"f7b5ee1af1ebcc6028d9cdb1c3af7748014104f46db5e9d61a9dc27b8d64ad23e7383a"
"4e6ca164593c2527c038c0857eb67ee8e825dca65046b82c9331586c82e0fd1f633f25"
"f87c161bc6f8a630121df2b3d3ffffffff0200e32321000000001976a914c398efa9c3"
"92ba6013c5e04ee729755ef7f58b3288ac000fe208010000001976a914948c765a6914"
"d43f2a7ac177da2c2f6b52de3d7c88ac000000000100000001c33ebff2a709f13d9f9a"
"7569ab16a32786af7d7e2de09265e41c61d078294ecf010000008a4730440220032d30"
"df5ee6f57fa46cddb5eb8d0d9fe8de6b342d27942ae90a3231e0ba333e02203deee806"
"0fdc70230a7f5b4ad7d7bc3e628cbe219a886b84269eaeb81e26b4fe014104ae31c31b"
"f91278d99b8377a35bbce5b27d9fff15456839e919453fc7b3f721f0ba403ff96c9dee"
"b680e5fd341c0fc3a7b90da4631ee39560639db462e9cb850fffffffff0240420f0000"
"0000001976a914b0dcbf97eabf4404e31d952477ce822dadbe7e1088acc060d2110000"
"00001976a9146b1281eec25ab4e1e0793ff4e08ab1abb3409cd988ac00000000010000"
"00010b6072b386d4a773235237f64c1126ac3b240c84b917a3909ba1c43ded5f51f400"
"0000008c493046022100bb1ad26df930a51cce110cf44f7a48c3c561fd977500b1ae5d"
"6b6fd13d0b3f4a022100c5b42951acedff14abba2736fd574bdb465f3e6f8da12e2c53"
"03954aca7f78f3014104a7135bfe824c97ecc01ec7d7e336185c81e2aa2c41ab175407"
"c09484ce9694b44953fcb751206564a9c24dd094d42fdbfdd5aad3e063ce6af4cfaaea"
"4ea14fbbffffffff0140420f00000000001976a91439aa3d569e06a1d7926dc4be1193"
"c99bf2eb9ee088ac00000000");
auto bl = DeserializeBlockHex(blockHex);
Log() << "Decoded block: " << bl.ToString() << " nTxns: " << bl.vtx.size();
QByteArray ba;
bitcoin::GenericVectorWriter<QByteArray> vr(bitcoin::SER_NETWORK, bitcoin::PROTOCOL_VERSION, ba, 0);
bl.Serialize(vr);
Log() << "Reserialized is equal: " << (ba.toHex() == blockHex ? "YES" : "NO");
*/
}
bool Addr() { return BTC::Address::test(); }
void HexParsers(const QString &filename)
{
const auto varlist = Util::Json::parseFile(filename, false).toList(); // throws on error
QList<QByteArray> hexList;
size_t bytes = 0;
for (const auto & v : varlist) {
auto ba = v.toByteArray();
ba = ba.trimmed().simplified();
if (ba.isEmpty())
throw Exception(QString("read an empty bytearray for item %1 -- make sure json has hex strings").arg(hexList.size()));
if (QByteArray::fromHex(ba).toHex() != ba)
throw Exception(QString("read bad hex data at %1: %2").arg(hexList.count()).arg(v.toString()));
bytes += size_t(ba.size());
hexList.push_back(ba);
}
Log() << "Read " << bytes << " hex-digits in " << hexList.count() << " bytearrays...";
using BVec = std::vector<QByteArray>;
BVec vec1, vec2;
using UVec = std::vector<std::vector<uint8_t>>;
UVec vec3;
vec1.reserve(size_t(hexList.size()));
vec2.reserve(size_t(hexList.size()));
vec3.reserve(size_t(hexList.size()));
const auto qtMethod = [&vec1, &hexList, &bytes]() -> qint64 {
size_t bytes2 = 0;
Log() << "Parsing hex using Qt's QByteArray::fromHex() ...";
const auto t0 = Util::getTimeNS();
for (const auto & hex : hexList) {
vec1.emplace_back(QByteArray::fromHex(hex));
}
const auto tf = Util::getTimeNS();
for (const auto & b : vec1)
bytes2 += size_t(b.size());
if (bytes2 * 2 != bytes)
throw Exception(QString("Decoded data is missing bytes: %1 != %2").arg(bytes2*2).arg(bytes));
const auto micros = qint64((tf-t0)/1000LL);
Log() << "Qt method: decoded " << bytes2 << " bytes, elapsed: " << micros << " usec";
return micros;
};
const auto customMethod = [&vec2, &hexList, &bytes]() -> qint64 {
size_t bytes2 = 0;
Log() << "Parsing hex using custom parseHex() ...";
const auto t0 = Util::getTimeNS();
for (const auto & hex : hexList) {
vec2.emplace_back(Util::ParseHexFast(hex));
}
const auto tf = Util::getTimeNS();
for (const auto & b : vec2)
bytes2 += size_t(b.size());
if (bytes2 * 2 != bytes)
throw Exception(QString("Decoded data is missing bytes: %1 != %2").arg(bytes2*2).arg(bytes));
const auto micros = qint64((tf-t0)/1000LL);
Log() << "Custom method: decoded " << bytes2 << " bytes, elapsed: " << micros << " usec";
return micros;
};
const auto abcMethod = [&vec3, &hexList, &bytes]() -> qint64 {
size_t bytes2 = 0;
Log() << "Parsing hex using bitcoin::ParseHex() from bitcoin-abc ...";
const auto t0 = Util::getTimeNS();
for (const auto & hex : hexList) {
vec3.emplace_back(bitcoin::ParseHex(hex.constData()));
}
const auto tf = Util::getTimeNS();
for (const auto & b : vec3)
bytes2 += size_t(b.size());
if (bytes2 * 2 != bytes)
throw Exception(QString("Decoded data is missing bytes: %1 != %2").arg(bytes2*2).arg(bytes));
const auto micros = qint64((tf-t0)/1000LL);
Log() << "abc method: decoded " << bytes2 << " bytes, elapsed: " << micros << " usec";
return micros;
};
qtMethod();
customMethod();
abcMethod();
if (vec1 == vec2)
Log() << "The first two resulting vectors match perfectly";
else
throw Exception("The first two vectors don't match!");
if (vec3.size() != vec2.size())
throw Exception("The abc method vector is of the wrong size");
for (size_t i = 0; i < vec3.size(); ++i) {
if (std::memcmp(vec3[i].data(), vec2[i].data(), vec3[i].size()) != 0)
throw Exception(QString("The abc method hex string %1 does not match").arg(i));
}
Log() << "The abc method data matches the other two data sets ok";
Log() << "Checking ToHexFast vs. Qt ...";
for (const auto & ba : vec1) {
if (Util::ToHexFast(ba) != ba.toHex())
throw Exception("ToHexFast and Qt toHex produced different hex strings!");
}
BVec res; res.reserve(vec1.size());
auto t0 = Util::getTimeNS();
for (const auto & ba : vec1) {
res.emplace_back(Util::ToHexFast(ba));
}
auto elapsed = (Util::getTimeNS() - t0)/1000LL;
Log() << "Util::ToHexFast took: " << elapsed << " usec";
res.clear(); res.reserve(vec1.size());
t0 = Util::getTimeNS();
for (const auto & ba : vec1) {
res.emplace_back(ba.toHex());
}
elapsed = (Util::getTimeNS() - t0)/1000LL;
Log() << "Qt toHex took: " << elapsed << " usec";
}
} // end namespace Tests
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif