1
////////////////////////////////////////////////////////////////////////////////
3
// Copyright (C) 2011-2014, Armory Technologies, Inc. //
4
// Distributed under the GNU Affero General Public License (AGPL v3) //
5
// See LICENSE or http://www.gnu.org/licenses/agpl.html //
7
////////////////////////////////////////////////////////////////////////////////
11
#include "StoredBlockObj.h"
13
DB_PRUNE_TYPE GlobalDBUtilities::dbPruneType_ = DB_PRUNE_WHATEVER;
14
ARMORY_DB_TYPE GlobalDBUtilities::armoryDbType_ = ARMORY_DB_WHATEVER;
15
GlobalDBUtilities* GlobalDBUtilities::theOneUtilsObj_ = NULL;
17
/////////////////////////////////////////////////////////////////////////////
18
BinaryData StoredDBInfo::getDBKey(void)
20
// Return a key that is guaranteed to be before all other non-empty
22
static BinaryData dbinfokey(0);
23
if(dbinfokey.getSize() == 0)
26
bw.put_uint8_t((uint8_t)DB_PREFIX_DBINFO);
27
dbinfokey = bw.getData();
32
/////////////////////////////////////////////////////////////////////////////
33
void StoredDBInfo::unserializeDBValue(BinaryRefReader & brr)
35
if(brr.getSizeRemaining() < 44)
38
topBlkHgt_ = UINT32_MAX;
39
topBlkHash_.resize(0);
42
brr.get_BinaryData(magic_, 4);
43
BitUnpacker<uint32_t> bitunpack(brr);
44
topBlkHgt_ = brr.get_uint32_t();
45
appliedToHgt_ = brr.get_uint32_t();
46
brr.get_BinaryData(topBlkHash_, 32);
48
armoryVer_ = bitunpack.getBits(4);
49
armoryType_ = (ARMORY_DB_TYPE)bitunpack.getBits(4);
50
pruneType_ = (DB_PRUNE_TYPE) bitunpack.getBits(4);
53
/////////////////////////////////////////////////////////////////////////////
54
void StoredDBInfo::serializeDBValue(BinaryWriter & bw ) const
56
BitPacker<uint32_t> bitpack;
57
bitpack.putBits((uint32_t)armoryVer_, 4);
58
bitpack.putBits((uint32_t)armoryType_, 4);
59
bitpack.putBits((uint32_t)pruneType_, 4);
61
bw.put_BinaryData(magic_);
62
bw.put_BitPacker(bitpack);
63
bw.put_uint32_t(topBlkHgt_); // top blk height
64
bw.put_uint32_t(appliedToHgt_); // top blk height
65
bw.put_BinaryData(topBlkHash_);
68
////////////////////////////////////////////////////////////////////////////////
69
void StoredDBInfo::unserializeDBValue(BinaryData const & bd)
71
BinaryRefReader brr(bd);
72
unserializeDBValue(brr);
75
////////////////////////////////////////////////////////////////////////////////
76
void StoredDBInfo::unserializeDBValue(BinaryDataRef bdr)
79
BinaryRefReader brr(bdr);
80
unserializeDBValue(brr);
83
////////////////////////////////////////////////////////////////////////////////
84
BinaryData StoredDBInfo::serializeDBValue(void) const
91
/////////////////////////////////////////////////////////////////////////////
92
void StoredDBInfo::pprintOneLine(uint32_t indent)
94
for(uint32_t i=0; i<indent; i++)
98
<< " TopBlk: " << topBlkHgt_
99
<< " , " << topBlkHash_.getSliceCopy(0,4).toHexStr().c_str()
103
/////////////////////////////////////////////////////////////////////////////
104
void StoredHeader::setKeyData(uint32_t hgt, uint8_t dupID)
106
// Set the params for this SBH object
108
duplicateID_ = dupID;
110
// Then trickle down to each StoredTx object (if any)
111
map<uint16_t, StoredTx>::iterator iter;
112
for(iter = stxMap_.begin(); iter != stxMap_.end(); iter++)
113
iter->second.setKeyData(hgt, dupID, iter->first);
116
/////////////////////////////////////////////////////////////////////////////
117
void StoredHeader::setHeightAndDup(uint32_t hgt, uint8_t dupID)
120
duplicateID_ = dupID;
123
/////////////////////////////////////////////////////////////////////////////
124
void StoredHeader::setHeightAndDup(BinaryData hgtx)
126
blockHeight_ = DBUtils.hgtxToHeight(hgtx);
127
duplicateID_ = DBUtils.hgtxToDupID(hgtx);
130
/////////////////////////////////////////////////////////////////////////////
131
bool StoredHeader::haveFullBlock(void) const
133
if(dataCopy_.getSize() != HEADER_SIZE)
136
for(uint16_t tx=0; tx<numTx_; tx++)
138
map<uint16_t, StoredTx>::const_iterator iter = stxMap_.find(tx);
139
if(ITER_NOT_IN_MAP(iter, stxMap_))
141
if(!iter->second.haveAllTxOut())
148
/////////////////////////////////////////////////////////////////////////////
149
BinaryData StoredHeader::getSerializedBlock(void) const
152
return BinaryData(0);
156
bw.reserve(numBytes_+100); // add extra room for header and var_int
158
bw.put_BinaryData(dataCopy_);
159
bw.put_var_int(numTx_);
160
for(uint16_t tx=0; tx<numTx_; tx++)
161
bw.put_BinaryData(stxMap_.at(tx).getSerializedTx());
167
/////////////////////////////////////////////////////////////////////////////
168
BinaryData StoredHeader::getDBKey(bool withPrefix) const
170
if(blockHeight_==UINT32_MAX || duplicateID_==UINT8_MAX)
172
LOGERR << "Requesting DB key for incomplete SBH";
173
return BinaryData(0);
177
return DBUtils.getBlkDataKey(blockHeight_, duplicateID_);
179
return DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_);
185
/////////////////////////////////////////////////////////////////////////////
186
void StoredHeader::createFromBlockHeader(BlockHeader & bh)
188
if(!bh.isInitialized())
190
LOGERR << "trying to create from uninitialized block header";
194
unserialize(bh.serialize());
196
numTx_ = bh.getNumTx();
197
numBytes_ = bh.getBlockSize();
198
blockHeight_ = bh.getBlockHeight();
199
duplicateID_ = UINT8_MAX;
200
isMainBranch_ = bh.isMainBranch();
201
hasBlockHeader_ = true;
204
////////////////////////////////////////////////////////////////////////////////
205
Tx StoredHeader::getTxCopy(uint16_t i)
207
if(KEY_IN_MAP(i, stxMap_))
208
return stxMap_[i].getTxCopy();
213
////////////////////////////////////////////////////////////////////////////////
214
BinaryData StoredHeader::getSerializedTx(uint16_t i)
216
if(KEY_IN_MAP(i, stxMap_))
217
return stxMap_[i].getSerializedTx();
219
return BinaryData(0);
222
/////////////////////////////////////////////////////////////////////////////
223
void StoredHeader::unserialize(BinaryData const & header80B)
225
unserialize(header80B.getRef());
228
/////////////////////////////////////////////////////////////////////////////
229
void StoredHeader::unserialize(BinaryRefReader brr)
231
unserialize(brr.getRawRef());
234
/////////////////////////////////////////////////////////////////////////////
235
void StoredHeader::unserialize(BinaryDataRef header80B)
237
if(header80B.getSize() != HEADER_SIZE)
239
LOGERR << "Asked to unserialize a non-80-byte header";
242
dataCopy_.copyFrom(header80B);
243
BtcUtils::getHash256(header80B, thisHash_);
246
/////////////////////////////////////////////////////////////////////////////
247
void StoredHeader::unserializeFullBlock(BinaryRefReader brr,
253
BinaryData magic = brr.get_BinaryData(4);
254
uint32_t nBytes = brr.get_uint32_t();
256
if(brr.getSizeRemaining() < nBytes)
258
LOGERR << "Not enough bytes remaining in BRR to read block";
264
uint32_t nTx = (uint32_t)brr.get_var_int();
266
createFromBlockHeader(bh);
269
numBytes_ = HEADER_SIZE + BtcUtils::calcVarIntSize(numTx_);
270
if(dataCopy_.getSize() != HEADER_SIZE)
272
LOGERR << "Unserializing header did not produce 80-byte object!";
276
BtcUtils::getHash256(dataCopy_, thisHash_);
278
for(uint32_t tx=0; tx<nTx; tx++)
280
// We're going to have to come back to the beginning of the tx, later
281
uint32_t txStart = brr.getPosition();
283
// Read a regular tx and then convert it
285
numBytes_ += thisTx.getSize();
287
// Now add it to the map
288
stxMap_[tx] = StoredTx();
289
StoredTx & stx = stxMap_[tx];
291
// Now copy the appropriate data from the vanilla Tx object
292
stx.createFromTx(thisTx, doFrag, true);
293
stx.isFragged_ = doFrag;
294
stx.version_ = thisTx.getVersion();
298
// Regardless of whether the tx is fragged, we still need the STXO map
299
// to be updated and consistent
301
brr.advance(txStart + thisTx.getTxOutOffset(0));
302
for(uint32_t txo=0; txo < thisTx.getNumTxOut(); txo++)
304
stx.stxoMap_[txo] = StoredTxOut();
305
StoredTxOut & stxo = stx.stxoMap_[txo];
307
stxo.unserialize(brr);
308
stxo.txVersion_ = thisTx.getVersion();
309
stxo.blockHeight_ = UINT32_MAX;
310
stxo.duplicateID_ = UINT8_MAX;
312
stxo.txOutIndex_ = txo;
313
stxo.isCoinbase_ = thisTx.getTxInCopy(0).isCoinbase();
316
// Sitting at the nLockTime, 4 bytes before the end
324
/////////////////////////////////////////////////////////////////////////////
325
void StoredHeader::unserializeFullBlock(BinaryDataRef block,
329
BinaryRefReader brr(block);
330
unserializeFullBlock(brr, doFrag, withPrefix);
333
/////////////////////////////////////////////////////////////////////////////
334
bool StoredHeader::serializeFullBlock(BinaryWriter & bw) const
338
LOGERR << "Attempted to serialize full block, but only have partial";
342
if(numTx_ == UINT32_MAX)
344
LOGERR << "Number of tx not available while serializing full block";
348
BinaryWriter bwTemp(1024*1024); // preallocate 1 MB which is the limit
349
bwTemp.put_BinaryData(dataCopy_);
350
bwTemp.put_var_int(numTx_);
351
map<uint16_t, StoredTx>::const_iterator iter;
352
for(iter = stxMap_.begin(); iter != stxMap_.end(); iter++)
354
if(!iter->second.haveAllTxOut())
356
LOGERR << "Don't have all TxOut in tx during serialize full block";
359
bwTemp.put_BinaryData(iter->second.getSerializedTx());
362
bw.put_BinaryData(bwTemp.getDataRef());
366
/////////////////////////////////////////////////////////////////////////////
367
void StoredHeader::addTxToMap(uint16_t txIdx, Tx & tx)
370
storedTx.createFromTx(tx);
371
addStoredTxToMap(txIdx, storedTx);
374
/////////////////////////////////////////////////////////////////////////////
375
void StoredHeader::addStoredTxToMap(uint16_t txIdx, StoredTx & stx)
379
LOGERR << "TxIdx is greater than numTx of stored header";
382
stxMap_[txIdx] = stx;
385
/////////////////////////////////////////////////////////////////////////////
386
void StoredTx::addTxOutToMap(uint16_t idx, TxOut & txout)
390
LOGERR << "TxOutIdx is greater than numTxOut of stored tx";
394
stxo.unserialize(txout.serialize());
395
stxoMap_[idx] = stxo;
398
/////////////////////////////////////////////////////////////////////////////
399
void StoredTx::addStoredTxOutToMap(uint16_t idx, StoredTxOut & stxo)
403
LOGERR << "TxOutIdx is greater than numTxOut of stored tx";
406
stxoMap_[idx] = stxo;
409
/////////////////////////////////////////////////////////////////////////////
410
BlockHeader StoredHeader::getBlockHeaderCopy(void) const
413
return BlockHeader();
415
BlockHeader bh(dataCopy_);
418
bh.setBlockSize(numBytes_);
419
bh.setDuplicateID(duplicateID_);
424
/////////////////////////////////////////////////////////////////////////////
425
BinaryData StoredHeader::getSerializedBlockHeader(void) const
428
return BinaryData(0);
433
////////////////////////////////////////////////////////////////////////////////
434
void StoredHeader::unserializeDBValue(DB_SELECT db,
435
BinaryData const & bd,
438
BinaryRefReader brr(bd);
439
unserializeDBValue(db, brr, ignoreMerkle);
442
////////////////////////////////////////////////////////////////////////////////
443
void StoredHeader::unserializeDBValue(DB_SELECT db,
447
BinaryRefReader brr(bdr);
448
unserializeDBValue(db, brr, ignoreMerkle);
450
////////////////////////////////////////////////////////////////////////////////
451
BinaryData StoredHeader::serializeDBValue(DB_SELECT db) const
454
serializeDBValue(db, bw);
458
/////////////////////////////////////////////////////////////////////////////
459
void StoredHeader::unserializeDBValue( DB_SELECT db,
460
BinaryRefReader & brr,
466
brr.get_BinaryData(dataCopy_, HEADER_SIZE);
467
BinaryData hgtx = brr.get_BinaryData(4);
468
blockHeight_ = DBUtils.hgtxToHeight(hgtx);
469
duplicateID_ = DBUtils.hgtxToDupID(hgtx);
470
BtcUtils::getHash256(dataCopy_, thisHash_);
474
// Read the flags byte
475
BitUnpacker<uint32_t> bitunpack(brr);
476
unserArmVer_ = bitunpack.getBits(4);
477
unserBlkVer_ = bitunpack.getBits(4);
478
unserDbType_ = (ARMORY_DB_TYPE) bitunpack.getBits(4);
479
unserPrType_ = (DB_PRUNE_TYPE) bitunpack.getBits(2);
480
unserMkType_ = (MERKLE_SER_TYPE)bitunpack.getBits(2);
481
blockAppliedToDB_ = bitunpack.getBit();
483
// Unserialize the raw header into the SBH object
484
brr.get_BinaryData(dataCopy_, HEADER_SIZE);
485
BtcUtils::getHash256(dataCopy_, thisHash_);
486
numTx_ = brr.get_uint32_t();
487
numBytes_ = brr.get_uint32_t();
489
if(unserArmVer_ != ARMORY_DB_VERSION)
490
LOGWARN << "Version mismatch in unserialize DB header";
494
uint32_t currPos = brr.getPosition();
495
uint32_t totalSz = brr.getSize();
496
if(unserMkType_ == MERKLE_SER_NONE)
500
merkleIsPartial_ = (unserMkType_ == MERKLE_SER_PARTIAL);
501
brr.get_BinaryData(merkle_, totalSz - currPos);
508
/////////////////////////////////////////////////////////////////////////////
509
void StoredHeader::serializeDBValue( DB_SELECT db,
510
BinaryWriter & bw) const
514
LOGERR << "Attempted to serialize uninitialized block header";
520
BinaryData hgtx = DBUtils.heightAndDupToHgtx(blockHeight_, duplicateID_);
521
bw.put_BinaryData(dataCopy_);
522
bw.put_BinaryData(hgtx);
526
uint32_t version = READ_UINT32_LE(dataCopy_.getPtr());
528
// TODO: We define merkle serialization types here, but we're not actually
529
// enforcing it in this function. Either merkle_ member contains
530
// the correct form of the merkle data or it doesn't. We should
531
// figure out whether we need to make sure the correct data is
532
// already here when this function starts, or guarantee the data
533
// is in the right form as part of this function. For now I'm
534
// assuming that it's already in the right form, and thus the
535
// determination of PARTIAL vs FULL is irrelevant
536
MERKLE_SER_TYPE mtype;
537
switch(DBUtils.getArmoryDbType())
539
// If we store all the tx anyway, don't need any/partial merkle trees
540
case ARMORY_DB_BARE: mtype = MERKLE_SER_NONE; break;
541
case ARMORY_DB_LITE: mtype = MERKLE_SER_PARTIAL; break;
542
case ARMORY_DB_PARTIAL: mtype = MERKLE_SER_FULL; break;
543
case ARMORY_DB_FULL: mtype = MERKLE_SER_NONE; break;
544
case ARMORY_DB_SUPER: mtype = MERKLE_SER_NONE; break;
546
LOGERR << "Invalid DB mode in serializeStoredHeaderValue";
549
// Override the above mtype if the merkle data is zero-length
550
if(merkle_.getSize()==0)
551
mtype = MERKLE_SER_NONE;
553
// Create the flags byte
554
BitPacker<uint32_t> bitpack;
555
bitpack.putBits((uint32_t)ARMORY_DB_VERSION, 4);
556
bitpack.putBits((uint32_t)version, 4);
557
bitpack.putBits((uint32_t)DBUtils.getArmoryDbType(), 4);
558
bitpack.putBits((uint32_t)DBUtils.getDbPruneType(), 2);
559
bitpack.putBits((uint32_t)mtype, 2);
560
bitpack.putBit(blockAppliedToDB_);
562
bw.put_BitPacker(bitpack);
563
bw.put_BinaryData(dataCopy_);
564
bw.put_uint32_t(numTx_);
565
bw.put_uint32_t(numBytes_);
567
if( mtype != MERKLE_SER_NONE )
569
bw.put_BinaryData(merkle_);
570
if(merkle_.getSize()==0)
571
LOGERR << "Expected to serialize merkle tree, but empty string";
577
/////////////////////////////////////////////////////////////////////////////
578
void StoredHeader::unserializeDBKey(DB_SELECT db, BinaryDataRef key)
582
BinaryRefReader brr(key);
583
if(key.getSize() == 4)
584
DBUtils.readBlkDataKeyNoPrefix(brr, blockHeight_, duplicateID_);
585
else if(key.getSize() == 5)
586
DBUtils.readBlkDataKey(brr, blockHeight_, duplicateID_);
588
LOGERR << "Invalid key for StoredHeader";
591
LOGERR << "This method not intended for HEADERS DB";
595
/////////////////////////////////////////////////////////////////////////////
596
void StoredHeader::pprintOneLine(uint32_t indent)
598
for(uint32_t i=0; i<indent; i++)
601
cout << "HEADER: " << thisHash_.getSliceCopy(0,4).toHexStr()
602
<< " (" << blockHeight_ << "," << (uint32_t)duplicateID_ << ")"
603
<< " #Tx: " << numTx_
604
<< " Applied: " << (blockAppliedToDB_ ? "T" : "F")
608
/////////////////////////////////////////////////////////////////////////////
609
void StoredHeader::pprintFullBlock(uint32_t indent)
611
pprintOneLine(indent);
614
cout << " <No tx to print>" << endl;
618
for(uint32_t i=0; i<numTx_; i++)
619
stxMap_[i].pprintFullTx(indent+3);
622
/////////////////////////////////////////////////////////////////////////////
623
BinaryData StoredTx::getDBKey(bool withPrefix) const
625
if(blockHeight_ == UINT32_MAX ||
626
duplicateID_ == UINT8_MAX ||
627
txIndex_ == UINT16_MAX)
629
LOGERR << "Requesting DB key for incomplete STX";
630
return BinaryData(0);
634
return DBUtils.getBlkDataKey(blockHeight_, duplicateID_, txIndex_);
636
return DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_, txIndex_);
639
/////////////////////////////////////////////////////////////////////////////
640
BinaryData StoredTx::getDBKeyOfChild(uint16_t i, bool withPrefix) const
642
return (getDBKey(withPrefix) + WRITE_UINT16_BE(i));
645
/////////////////////////////////////////////////////////////////////////////
646
void StoredTx::unserialize(BinaryData const & data, bool fragged)
648
BinaryRefReader brr(data);
649
unserialize(brr, fragged);
652
/////////////////////////////////////////////////////////////////////////////
653
void StoredTx::unserialize(BinaryDataRef data, bool fragged)
655
BinaryRefReader brr(data);
656
unserialize(brr, fragged);
660
/////////////////////////////////////////////////////////////////////////////
661
void StoredTx::unserialize(BinaryRefReader & brr, bool fragged)
663
vector<uint32_t> offsetsIn, offsetsOut;
664
uint32_t nbytes = BtcUtils::StoredTxCalcLength(brr.getCurrPtr(),
668
if(brr.getSizeRemaining() < nbytes)
670
LOGERR << "Not enough bytes in BRR to unserialize StoredTx";
674
brr.get_BinaryData(dataCopy_, nbytes);
676
isFragged_ = fragged;
677
numTxOut_ = offsetsOut.size()-1;
678
version_ = READ_UINT32_LE(dataCopy_.getPtr());
679
lockTime_ = READ_UINT32_LE(dataCopy_.getPtr() + nbytes - 4);
684
numBytes_ = UINT32_MAX;
689
uint32_t span = offsetsOut[numTxOut_] - offsetsOut[0];
690
fragBytes_ = numBytes_ - span;
691
BtcUtils::getHash256(dataCopy_, thisHash_);
696
////////////////////////////////////////////////////////////////////////////////
697
void StoredTx::unserializeDBValue(BinaryData const & bd)
699
BinaryRefReader brr(bd);
700
unserializeDBValue(brr);
703
////////////////////////////////////////////////////////////////////////////////
704
void StoredTx::unserializeDBValue(BinaryDataRef bdr)
707
BinaryRefReader brr(bdr);
708
unserializeDBValue(brr);
711
////////////////////////////////////////////////////////////////////////////////
712
BinaryData StoredTx::serializeDBValue(void) const
715
serializeDBValue(bw);
721
/////////////////////////////////////////////////////////////////////////////
722
void StoredTx::unserializeDBValue(BinaryRefReader & brr)
727
// HowTxSer 4 bits (FullTxOut, TxNoTxOuts, numTxOutOnly)
728
BitUnpacker<uint16_t> bitunpack(brr); // flags
729
unserArmVer_ = bitunpack.getBits(4);
730
unserTxVer_ = bitunpack.getBits(2);
731
unserTxType_ = (TX_SERIALIZE_TYPE)bitunpack.getBits(4);
733
if(unserArmVer_ != ARMORY_DB_VERSION)
734
LOGWARN << "Version mismatch in unserialize DB tx";
736
brr.get_BinaryData(thisHash_, 32);
738
if(unserTxType_ == TX_SER_FULL || unserTxType_ == TX_SER_FRAGGED)
739
unserialize(brr, unserTxType_==TX_SER_FRAGGED);
741
numTxOut_ = (uint32_t)brr.get_var_int();
745
/////////////////////////////////////////////////////////////////////////////
746
void StoredTx::serializeDBValue(BinaryWriter & bw) const
748
TX_SERIALIZE_TYPE serType;
750
switch(DBUtils.getArmoryDbType())
752
// In most cases, if storing separate TxOuts, fragged Tx is fine
753
// UPDATE: I'm not sure there's a good reason to NOT frag ever
754
case ARMORY_DB_BARE: serType = TX_SER_FRAGGED; break;
755
case ARMORY_DB_LITE: serType = TX_SER_FRAGGED; break;
756
case ARMORY_DB_PARTIAL: serType = TX_SER_FRAGGED; break;
757
case ARMORY_DB_FULL: serType = TX_SER_FRAGGED; break;
758
case ARMORY_DB_SUPER: serType = TX_SER_FRAGGED; break;
760
LOGERR << "Invalid DB mode in serializeStoredTxValue";
763
if(serType==TX_SER_FULL && !haveAllTxOut())
765
LOGERR << "Supposed to write out full Tx, but don't have it";
769
if(thisHash_.getSize() == 0)
771
LOGERR << "Do not know tx hash to be able to DB-serialize StoredTx";
775
uint16_t version = (uint16_t)READ_UINT32_LE(dataCopy_.getPtr());
777
BitPacker<uint16_t> bitpack;
778
bitpack.putBits((uint16_t)ARMORY_DB_VERSION, 4);
779
bitpack.putBits((uint16_t)version, 2);
780
bitpack.putBits((uint16_t)serType, 4);
783
bw.put_BitPacker(bitpack);
784
bw.put_BinaryData(thisHash_);
786
if(serType == TX_SER_FULL)
787
bw.put_BinaryData(getSerializedTx());
788
else if(serType == TX_SER_FRAGGED)
789
bw.put_BinaryData(getSerializedTxFragged());
791
bw.put_var_int(numTxOut_);
795
////////////////////////////////////////////////////////////////////////////////
796
bool StoredTx::haveAllTxOut(void) const
804
return stxoMap_.size()==numTxOut_;
809
////////////////////////////////////////////////////////////////////////////////
810
BinaryData StoredTx::getSerializedTx(void) const
813
return BinaryData(0);
817
else if(!haveAllTxOut())
818
return BinaryData(0);
821
bw.reserve(numBytes_);
823
if(numBytes_ != UINT32_MAX)
824
bw.reserve(numBytes_);
826
bw.put_BinaryData(dataCopy_.getPtr(), dataCopy_.getSize()-4);
828
map<uint16_t, StoredTxOut>::const_iterator iter;
830
for(iter = stxoMap_.begin(); iter != stxoMap_.end(); iter++, i++)
834
LOGERR << "Indices out of order accessing stxoMap_...?!";
835
return BinaryData(0);
837
bw.put_BinaryData(iter->second.getSerializedTxOut());
840
bw.put_BinaryData(dataCopy_.getPtr()+dataCopy_.getSize()-4, 4);
844
////////////////////////////////////////////////////////////////////////////////
845
BinaryData StoredTx::getSerializedTxFragged(void) const
848
return BinaryData(0);
853
if(numBytes_ == UINT32_MAX)
855
LOGERR << "Do not know size of tx in order to serialize it";
856
return BinaryData(0);
860
vector<uint32_t> outOffsets;
861
BtcUtils::StoredTxCalcLength(dataCopy_.getPtr(), false, NULL, &outOffsets);
862
uint32_t firstOut = outOffsets[0];
863
uint32_t afterLast = outOffsets[outOffsets.size()-1];
864
uint32_t span = afterLast - firstOut;
866
BinaryData output(dataCopy_.getSize() - span);
867
dataCopy_.getSliceRef(0, firstOut).copyTo(output.getPtr());
868
dataCopy_.getSliceRef(afterLast, 4).copyTo(output.getPtr()+firstOut);
872
/////////////////////////////////////////////////////////////////////////////
873
void StoredTx::unserializeDBKey(BinaryDataRef key)
875
BinaryRefReader brr(key);
876
if(key.getSize() == 6)
877
DBUtils.readBlkDataKeyNoPrefix(brr, blockHeight_, duplicateID_, txIndex_);
878
else if(key.getSize() == 7)
879
DBUtils.readBlkDataKey(brr, blockHeight_, duplicateID_, txIndex_);
881
LOGERR << "Invalid key for StoredTx";
884
////////////////////////////////////////////////////////////////////////////////
885
void StoredTx::pprintOneLine(uint32_t indent)
887
for(uint32_t i=0; i<indent; i++)
890
cout << "TX: " << thisHash_.getSliceCopy(0,4).toHexStr()
891
<< " (" << blockHeight_
892
<< "," << (uint32_t)duplicateID_
893
<< "," << txIndex_ << ")"
894
<< " #TXO: " << numTxOut_
898
////////////////////////////////////////////////////////////////////////////////
899
void StoredTx::pprintFullTx(uint32_t indent)
901
pprintOneLine(indent);
902
if(numTxOut_ > 10000)
904
cout << " <No txout to print>" << endl;
908
for(uint32_t i=0; i<numTxOut_; i++)
909
stxoMap_[i].pprintOneLine(indent+3);
912
////////////////////////////////////////////////////////////////////////////////
913
void StoredTxOut::unserialize(BinaryData const & data)
915
BinaryRefReader brr(data);
919
////////////////////////////////////////////////////////////////////////////////
920
void StoredTxOut::unserialize(BinaryDataRef data)
922
BinaryRefReader brr(data);
926
////////////////////////////////////////////////////////////////////////////////
927
void StoredTxOut::unserialize(BinaryRefReader & brr)
929
if(brr.getSizeRemaining() < 8)
931
LOGERR << "Not enough bytes in BRR to unserialize StoredTxOut";
935
uint32_t numBytes = BtcUtils::TxOutCalcLength(brr.getCurrPtr());
937
if(brr.getSizeRemaining() < numBytes)
939
LOGERR << "Not enough bytes in BRR to unserialize StoredTxOut";
943
brr.get_BinaryData(dataCopy_, numBytes);
947
////////////////////////////////////////////////////////////////////////////////
948
void StoredTxOut::unserializeDBValue(BinaryData const & bd)
950
BinaryRefReader brr(bd);
951
unserializeDBValue(brr);
954
////////////////////////////////////////////////////////////////////////////////
955
void StoredTxOut::unserializeDBValue(BinaryDataRef bdr)
957
BinaryRefReader brr(bdr);
958
unserializeDBValue(brr);
961
////////////////////////////////////////////////////////////////////////////////
962
void StoredTxOut::unserializeDBValue(BinaryRefReader & brr)
964
// Similar to TxValue flags
968
BitUnpacker<uint16_t> bitunpack(brr);
969
unserArmVer_ = bitunpack.getBits(4);
970
txVersion_ = bitunpack.getBits(2);
971
spentness_ = (TXOUT_SPENTNESS)bitunpack.getBits(2);
972
isCoinbase_ = bitunpack.getBit();
975
if(spentness_ == TXOUT_SPENT && brr.getSizeRemaining()>=8)
976
spentByTxInKey_ = brr.get_BinaryData(8);
981
////////////////////////////////////////////////////////////////////////////////
982
BinaryData StoredTxOut::serializeDBValue(bool forceSaveSpent) const
985
serializeDBValue(bw, forceSaveSpent);
989
////////////////////////////////////////////////////////////////////////////////
990
void StoredTxOut::serializeDBValue(BinaryWriter & bw,
991
bool forceSaveSpentness) const
993
TXOUT_SPENTNESS writeSpent = spentness_;
995
if(!forceSaveSpentness)
997
switch(DBUtils.getArmoryDbType())
999
//// If the DB is in lite or partial modes, we don't bother recording
1000
// spentness (in fact, if it's spent, this entry probably won't even
1001
// be written to the DB).
1002
case ARMORY_DB_BARE: break;
1003
case ARMORY_DB_LITE: writeSpent = TXOUT_SPENTUNK; break;
1004
case ARMORY_DB_PARTIAL: writeSpent = TXOUT_SPENTUNK; break;
1005
case ARMORY_DB_FULL: break;
1006
case ARMORY_DB_SUPER: break;
1008
LOGERR << "Invalid DB mode in serializeStoredTxOutValue";
1012
uint16_t isCbase = (isCoinbase_ ? 1 : 0);
1014
BitPacker<uint16_t> bitpack;
1015
bitpack.putBits((uint16_t)ARMORY_DB_VERSION, 4);
1016
bitpack.putBits((uint16_t)txVersion_, 2);
1017
bitpack.putBits((uint16_t)writeSpent, 2);
1018
bitpack.putBit( isCoinbase_);
1020
bw.put_BitPacker(bitpack);
1021
bw.put_BinaryData(dataCopy_); // 8-byte value, var_int sz, pkscript
1023
if(writeSpent == TXOUT_SPENT)
1025
if(spentByTxInKey_.getSize()==0)
1026
LOGERR << "Need to write out spentByTxIn but no spentness data";
1027
bw.put_BinaryData(spentByTxInKey_);
1032
/////////////////////////////////////////////////////////////////////////////
1033
BinaryData StoredTxOut::getDBKey(bool withPrefix) const
1035
if(blockHeight_ == UINT32_MAX ||
1036
duplicateID_ == UINT8_MAX ||
1037
txIndex_ == UINT16_MAX ||
1038
txOutIndex_ == UINT16_MAX)
1040
LOGERR << "Requesting DB key for incomplete STXO";
1041
return BinaryData(0);
1045
return DBUtils.getBlkDataKey(
1046
blockHeight_, duplicateID_, txIndex_, txOutIndex_);
1048
return DBUtils.getBlkDataKeyNoPrefix(
1049
blockHeight_, duplicateID_, txIndex_, txOutIndex_);
1052
////////////////////////////////////////////////////////////////////////////////
1053
BinaryData StoredTxOut::getDBKeyOfParentTx(bool withPrefix) const
1055
BinaryData stxoKey = getDBKey(withPrefix);
1057
return stxoKey.getSliceCopy(0, 7);
1059
return stxoKey.getSliceCopy(0, 6);
1062
////////////////////////////////////////////////////////////////////////////////
1063
bool StoredTxOut::matchesDBKey(BinaryDataRef dbkey) const
1065
if(dbkey.getSize() == 8)
1066
return (getDBKey(false) == dbkey);
1067
else if(dbkey.getSize() == 9)
1068
return (getDBKey(true) == dbkey);
1071
LOGERR << "Non STXO-DBKey passed in to check match against STXO";
1077
////////////////////////////////////////////////////////////////////////////////
1078
Tx StoredTx::getTxCopy(void) const
1082
LOGERR << "Cannot get tx copy, because don't have full StoredTx!";
1086
Tx returnTx(getSerializedTx());
1087
if(blockHeight_ != UINT32_MAX)
1088
returnTx.setTxRef(TxRef(getDBKey(false)));
1092
////////////////////////////////////////////////////////////////////////////////
1093
void StoredTx::setKeyData(uint32_t height, uint8_t dup, uint16_t txIdx)
1095
blockHeight_ = height;
1099
map<uint16_t, StoredTxOut>::iterator iter;
1100
for(iter = stxoMap_.begin(); iter != stxoMap_.end(); iter++)
1102
iter->second.blockHeight_ = height;
1103
iter->second.duplicateID_ = dup;
1104
iter->second.txIndex_ = txIdx;
1105
iter->second.txOutIndex_ = iter->first;
1109
////////////////////////////////////////////////////////////////////////////////
1110
StoredTx & StoredTx::createFromTx(BinaryDataRef rawTx, bool doFrag, bool withTxOuts)
1113
return createFromTx(tx, doFrag, withTxOuts);
1116
////////////////////////////////////////////////////////////////////////////////
1117
StoredTx & StoredTx::createFromTx(Tx & tx, bool doFrag, bool withTxOuts)
1119
if(!tx.isInitialized())
1121
LOGERR << "Creating storedtx from uninitialized tx. Aborting.";
1122
dataCopy_.resize(0);
1126
thisHash_ = tx.getThisHash();
1127
numTxOut_ = tx.getNumTxOut();
1128
version_ = tx.getVersion();
1129
lockTime_ = tx.getLockTime();
1130
numBytes_ = tx.getSize();
1131
isFragged_ = doFrag;
1133
uint32_t span = tx.getTxOutOffset(numTxOut_) - tx.getTxOutOffset(0);
1134
fragBytes_ = numBytes_ - span;
1137
dataCopy_ = tx.serialize();
1140
BinaryRefReader brr(tx.getPtr(), tx.getSize());
1141
uint32_t firstOut = tx.getTxOutOffset(0);
1142
uint32_t afterLast = tx.getTxOutOffset(numTxOut_);
1143
uint32_t span = afterLast - firstOut;
1144
dataCopy_.resize(tx.getSize() - span);
1145
brr.get_BinaryData(dataCopy_.getPtr(), firstOut);
1147
brr.get_BinaryData(dataCopy_.getPtr()+firstOut, 4);
1152
for(uint32_t txo = 0; txo < tx.getNumTxOut(); txo++)
1154
stxoMap_[txo] = StoredTxOut();
1155
StoredTxOut & stxo = stxoMap_[txo];
1157
stxo.unserialize(tx.getTxOutCopy(txo).serialize());
1158
stxo.txVersion_ = tx.getVersion();
1159
stxo.txIndex_ = tx.getBlockTxIndex();
1160
stxo.txOutIndex_ = txo;
1161
stxo.isCoinbase_ = tx.getTxInCopy(0).isCoinbase();
1169
////////////////////////////////////////////////////////////////////////////////
1170
StoredTxOut & StoredTxOut::createFromTxOut(TxOut & txout)
1172
unserialize(txout.serialize());
1176
////////////////////////////////////////////////////////////////////////////////
1177
BinaryData StoredTxOut::getSerializedTxOut(void) const
1179
if(!isInitialized())
1181
LOGERR << "Attempted to get serialized TxOut, but not initialized";
1182
return BinaryData(0);
1187
////////////////////////////////////////////////////////////////////////////////
1188
TxOut StoredTxOut::getTxOutCopy(void) const
1190
if(!isInitialized())
1192
LOGERR << "Attempted to get TxOut copy but not initialized";
1196
o.unserialize_checked(dataCopy_.getPtr(), dataCopy_.getSize());
1200
////////////////////////////////////////////////////////////////////////////////
1201
BinaryData StoredTxOut::getScrAddress(void) const
1203
BinaryRefReader brr(dataCopy_);
1205
uint32_t scrsz = (uint32_t)brr.get_var_int();
1206
return BtcUtils::getTxOutScrAddr(brr.get_BinaryDataRef(scrsz));
1209
////////////////////////////////////////////////////////////////////////////////
1210
BinaryDataRef StoredTxOut::getScriptRef(void) const
1212
BinaryRefReader brr(dataCopy_);
1214
uint32_t scrsz = (uint32_t)brr.get_var_int();
1215
return brr.get_BinaryDataRef(scrsz);
1218
////////////////////////////////////////////////////////////////////////////////
1219
uint64_t StoredTxOut::getValue(void) const
1221
if(!isInitialized())
1224
return *(uint64_t*)dataCopy_.getPtr();
1227
/////////////////////////////////////////////////////////////////////////////
1228
void StoredTxOut::unserializeDBKey(BinaryDataRef key)
1230
BinaryRefReader brr(key);
1231
if(key.getSize() == 8)
1232
DBUtils.readBlkDataKeyNoPrefix(brr, blockHeight_, duplicateID_, txIndex_, txOutIndex_);
1233
else if(key.getSize() == 9)
1234
DBUtils.readBlkDataKey(brr, blockHeight_, duplicateID_, txIndex_, txOutIndex_);
1236
LOGERR << "Invalid key for StoredTxOut";
1239
////////////////////////////////////////////////////////////////////////////////
1240
void StoredTxOut::pprintOneLine(uint32_t indent)
1242
for(uint32_t i=0; i<indent; i++)
1245
string pprintHash("");
1246
if(parentHash_.getSize() > 0)
1247
pprintHash = parentHash_.getSliceCopy(0,4).toHexStr();
1250
<< " (" << blockHeight_
1251
<< "," << (uint32_t)duplicateID_
1253
<< "," << txOutIndex_ << ")"
1254
<< " Value=" << (double)(getValue())/(100000000.0)
1255
<< " isCB: " << (isCoinbase_ ? "(X)" : " ");
1257
if(spentness_ == TXOUT_SPENTUNK)
1258
cout << " Spnt: " << "<-----UNKNOWN---->" << endl;
1259
else if(spentness_ == TXOUT_UNSPENT)
1260
cout << " Spnt: " << "< >" << endl;
1262
cout << " Spnt: " << "<" << spentByTxInKey_.toHexStr() << ">" << endl;
1265
////////////////////////////////////////////////////////////////////////////////
1266
// The list of spent/unspent txOuts is exactly what is needed to construct
1267
// a full vector<TxIOPair> for each address. Keep in mind that this list
1268
// only contains TxOuts and spentness of those TxOuts that are:
1269
// (1) Already in the blockchain
1270
// (2) On the longest chain at the time is was written
1271
// It contains no zero-confirmation tx data, and it may not be accurate
1272
// if there was a reorg since it was written. Part of the challenge of
1273
// implementing this DB stuff correctly is making sure both conditions
1274
// above are adhered to, despite TxIOPair objects being used in RAM to store
1275
// zero-confirmation data as well as in-blockchain data.
1276
void StoredScriptHistory::unserializeDBValue(BinaryRefReader & brr)
1278
// Now read the stored data fro this registered address
1279
DB_PRUNE_TYPE pruneType;
1280
SCRIPT_UTXO_TYPE txoListType;
1281
BitUnpacker<uint16_t> bitunpack(brr);
1282
version_ = bitunpack.getBits(4);
1283
pruneType = (DB_PRUNE_TYPE) bitunpack.getBits(2);
1284
txoListType = (SCRIPT_UTXO_TYPE) bitunpack.getBits(2);
1285
useMultipleEntries_ = bitunpack.getBit();
1287
alreadyScannedUpToBlk_ = brr.get_uint32_t();
1288
totalTxioCount_ = brr.get_var_int();
1290
// We shouldn't end up with empty SSH's, but should catch it just in case
1291
if(totalTxioCount_==0)
1294
subHistMap_.clear();
1295
if(useMultipleEntries_)
1296
totalUnspent_ = brr.get_uint64_t();
1299
// If we are not using multiple entries, then we have a TxIO in this
1300
// base SSH entry, and no sub-histories. Otherwise, there's nothing
1301
// else to be read from this DB value
1302
BitUnpacker<uint8_t> bitunpack(brr);
1303
bool isFromSelf = bitunpack.getBit();
1304
bool isCoinbase = bitunpack.getBit();
1305
bool isSpent = bitunpack.getBit();
1306
bool isMulti = bitunpack.getBit();
1308
// We always include the 8-byte value
1309
uint64_t txoValue = brr.get_uint64_t();
1311
// First 4 bytes is same for all TxIOs, and was copied outside the loop.
1312
// So we grab the last four bytes and copy it to the end.
1313
BinaryData fullTxOutKey = brr.get_BinaryData(8);
1314
TxIOPair txio(fullTxOutKey, txoValue);
1318
txio.setTxIn(brr.get_BinaryDataRef(8));
1322
totalUnspent_ = txoValue;
1325
txio.setTxOutFromSelf(isFromSelf);
1326
txio.setFromCoinbase(isCoinbase);
1327
txio.setMultisig(isMulti);
1329
// The second "true" is to tell the insert function to skip incrementing
1330
// the totalUnspent_ and totalTxioCount_, since that data is already
1332
insertTxio(txio, true, true);
1337
////////////////////////////////////////////////////////////////////////////////
1338
void StoredScriptHistory::serializeDBValue(BinaryWriter & bw ) const
1340
// Write out all the flags
1341
BitPacker<uint16_t> bitpack;
1342
bitpack.putBits((uint16_t)ARMORY_DB_VERSION, 4);
1343
bitpack.putBits((uint16_t)DBUtils.getDbPruneType(),2);
1344
bitpack.putBits((uint16_t)SCRIPT_UTXO_VECTOR, 2);
1345
bitpack.putBit(useMultipleEntries_);
1346
bw.put_BitPacker(bitpack);
1349
bw.put_uint32_t(alreadyScannedUpToBlk_);
1350
bw.put_var_int(totalTxioCount_);
1352
// We shouldn't end up with empty SSH's, but should catch it just in case
1353
if(totalTxioCount_==0)
1356
// Most addresses have only one TxIO, so we store it in the base SSH
1357
// DB entry. If there's more than one, we serialize nothing else,
1358
// and stored all the TxIOs in the sub-SSH entries (sub-histories).
1359
if(useMultipleEntries_)
1360
bw.put_uint64_t(totalUnspent_);
1363
if(subHistMap_.size() != 1)
1365
LOGERR << "!multi entry but " << subHistMap_.size() << " TxIOs?";
1366
LOGERR << uniqueKey_.toHexStr().c_str();
1370
map<BinaryData, StoredSubHistory>::const_iterator iter;
1371
iter = subHistMap_.begin();
1372
if(iter->second.txioSet_.size() != 1)
1374
LOGERR << "One subSSH but " << iter->second.txioSet_.size() << " TxIOs?";
1378
// Iter is pointing to the first SubSSH, now get the first/only TxIOPair
1379
TxIOPair const & txio = iter->second.txioSet_.begin()->second;
1380
BinaryData key8B = txio.getDBKeyOfOutput();
1382
BitPacker<uint8_t> bitpack;
1383
bitpack.putBit(txio.isTxOutFromSelf());
1384
bitpack.putBit(txio.isFromCoinbase());
1385
bitpack.putBit(txio.hasTxInInMain());
1386
bitpack.putBit(txio.isMultisig());
1387
bw.put_BitPacker(bitpack);
1389
// Always write the value and last 4 bytes of dbkey (first 4 is in dbkey)
1390
bw.put_uint64_t(txio.getValue());
1391
bw.put_BinaryData(key8B);
1393
// If not supposed to write the TxIn, we would've bailed earlier
1394
if(txio.hasTxInInMain())
1395
bw.put_BinaryData(txio.getDBKeyOfInput());
1400
////////////////////////////////////////////////////////////////////////////////
1401
void StoredScriptHistory::unserializeDBValue(BinaryData const & bd)
1403
BinaryRefReader brr(bd);
1404
unserializeDBValue(brr);
1407
////////////////////////////////////////////////////////////////////////////////
1408
void StoredScriptHistory::unserializeDBValue(BinaryDataRef bdr)
1411
BinaryRefReader brr(bdr);
1412
unserializeDBValue(brr);
1415
////////////////////////////////////////////////////////////////////////////////
1416
BinaryData StoredScriptHistory::serializeDBValue(void) const
1419
serializeDBValue(bw);
1420
return bw.getData();
1423
////////////////////////////////////////////////////////////////////////////////
1424
BinaryData StoredScriptHistory::getDBKey(bool withPrefix) const
1426
BinaryWriter bw(1+uniqueKey_.getSize());
1428
bw.put_uint8_t((uint8_t)DB_PREFIX_SCRIPT);
1430
bw.put_BinaryData(uniqueKey_);
1431
return bw.getData();
1435
////////////////////////////////////////////////////////////////////////////////
1436
SCRIPT_PREFIX StoredScriptHistory::getScriptType(void) const
1438
if(uniqueKey_.getSize() == 0)
1439
return SCRIPT_PREFIX_NONSTD;
1441
return (SCRIPT_PREFIX)uniqueKey_[0];
1445
/////////////////////////////////////////////////////////////////////////////
1446
void StoredScriptHistory::unserializeDBKey(BinaryDataRef key, bool withPrefix)
1450
uniqueKey_ = key.getSliceCopy(1, key.getSize()-1);
1455
////////////////////////////////////////////////////////////////////////////////
1456
void StoredScriptHistory::pprintOneLine(uint32_t indent)
1458
for(uint32_t i=0; i<indent; i++)
1462
if(uniqueKey_[0] == SCRIPT_PREFIX_HASH160)
1464
else if(uniqueKey_[0] == SCRIPT_PREFIX_P2SH)
1466
else if(uniqueKey_[0] == SCRIPT_PREFIX_MULTISIG)
1468
else if(uniqueKey_[0] == SCRIPT_PREFIX_NONSTD)
1471
uint32_t sz = uniqueKey_.getSize();
1472
cout << "SSHOBJ: " << ktype.c_str() << ": "
1473
<< uniqueKey_.getSliceCopy(1,sz-1).toHexStr()
1474
<< " Sync: " << alreadyScannedUpToBlk_
1475
<< " #IO: " << totalTxioCount_
1476
<< " Unspent: " << (totalUnspent_/COIN)
1481
////////////////////////////////////////////////////////////////////////////////
1482
void StoredScriptHistory::pprintFullSSH(uint32_t indent)
1484
pprintOneLine(indent);
1486
// Print all the txioVects
1487
map<BinaryData, StoredSubHistory>::iterator iter;
1488
for(iter = subHistMap_.begin(); iter != subHistMap_.end(); iter++)
1489
iter->second.pprintFullSubSSH(indent+3);
1494
////////////////////////////////////////////////////////////////////////////////
1495
TxIOPair* StoredScriptHistory::findTxio(BinaryData const & dbKey8B,
1496
bool includeMultisig)
1498
if(!isInitialized() || subHistMap_.size() == 0)
1501
// Optimize case of 1 txio -- don't bother with extra copies or map.find ops
1502
if(totalTxioCount_ == 1)
1504
// We gotta do some simple checks to avoid segfaulting in case
1505
// totalTxioCount_ is wrong (which shouldn't ever happen)
1506
if(subHistMap_.size() != 1)
1508
LOGERR << "totalTxioCount_ and subHistMap_.size do not agree!";
1512
StoredSubHistory & subSSH = subHistMap_.begin()->second;
1513
if(subSSH.txioSet_.size() != 1)
1515
LOGERR << "totalTxioCount_ and subSSH.txioSet_.size() do not agree!";
1519
TxIOPair* outptr = &(subSSH.txioSet_.begin()->second);
1520
if(!includeMultisig && outptr->isMultisig())
1523
return (outptr->getDBKeyOfOutput() == dbKey8B ? outptr : NULL);
1527
// Otherwise, we go searching...
1528
BinaryData first4 = dbKey8B.getSliceCopy(0,4);
1529
map<BinaryData, StoredSubHistory>::iterator iterSubSSH;
1530
iterSubSSH = subHistMap_.find(first4);
1531
if(ITER_NOT_IN_MAP(iterSubSSH, subHistMap_))
1534
// This returns NULL if does not exist.
1535
TxIOPair* outptr = iterSubSSH->second.findTxio(dbKey8B);
1539
if(!includeMultisig && outptr->isMultisig())
1546
////////////////////////////////////////////////////////////////////////////////
1547
TxIOPair& StoredScriptHistory::insertTxio(TxIOPair const & txio,
1551
BinaryData dbKey8 = txio.getDBKeyOfOutput();
1552
BinaryData first4 = dbKey8.getSliceCopy(0,4);
1553
map<BinaryData, StoredSubHistory>::iterator iterSubHist;
1554
iterSubHist = subHistMap_.find(first4);
1555
if(ITER_NOT_IN_MAP(iterSubHist, subHistMap_))
1557
// Create a new sub-history add it to its map
1558
subHistMap_[first4] = StoredSubHistory();
1559
subHistMap_[first4].uniqueKey_ = uniqueKey_;
1560
subHistMap_[first4].hgtX_ = first4;
1563
totalTxioCount_ += 1;
1564
if(!txio.hasTxInInMain() && !txio.isMultisig())
1565
totalUnspent_ += txio.getValue();
1566
useMultipleEntries_ = (totalTxioCount_>1);
1568
return subHistMap_[first4].insertTxio(txio, withOverwrite);
1572
// We have sub-history already, though not sure about this specific Txio
1573
if(iterSubHist->second.findTxio(dbKey8) == NULL && !skipTally)
1575
// We don't have it yet, the insert call will add it
1576
totalTxioCount_ += 1;
1577
if(!txio.hasTxInInMain() && !txio.isMultisig())
1578
totalUnspent_ += txio.getValue();
1579
useMultipleEntries_ = (totalTxioCount_>1);
1581
return iterSubHist->second.insertTxio(txio, withOverwrite);
1585
////////////////////////////////////////////////////////////////////////////////
1586
// For subtle bugginess reasons, even if we are pruning and reduce the total
1587
// TxIO count to one, we will keep "useMultipleEntries_=true". Once true, always
1588
// true, regardless of how many TxIO we have.
1589
bool StoredScriptHistory::eraseTxio(TxIOPair const & txio)
1591
return eraseTxio(txio.getDBKeyOfOutput());
1594
////////////////////////////////////////////////////////////////////////////////
1595
bool StoredScriptHistory::eraseTxio(BinaryData const & dbKey8B)
1597
if(!isInitialized())
1600
if(dbKey8B.getSize() != 8)
1602
LOGERR << "Invalid dbKey: " << dbKey8B.toHexStr().c_str();
1606
BinaryData first4 = dbKey8B.getSliceCopy(0,4);
1607
map<BinaryData, StoredSubHistory>::iterator iterSubHist;
1608
iterSubHist = subHistMap_.find(first4);
1609
if(ITER_NOT_IN_MAP(iterSubHist, subHistMap_))
1612
StoredSubHistory & subssh = iterSubHist->second;
1613
uint64_t valueRemoved = subssh.eraseTxio(dbKey8B);
1615
bool wasRemoved = (valueRemoved!=UINT64_MAX);
1618
totalTxioCount_ -= 1;
1619
totalUnspent_ -= valueRemoved;
1622
// Commented out because we need to be able to iterate through and see
1623
// which subHistMap_ are empty (later) so they can be removed from the DB.
1624
// If we erase it here without recording any kind of tracking data, later
1625
// we have no idea what was removed and those dead SubSSH objects are left
1627
//if(iterSubHist->second.txioSet_.size() == 0)
1628
//subHistMap_.erase(iterSubHist);
1634
////////////////////////////////////////////////////////////////////////////////
1635
bool StoredScriptHistory::mergeSubHistory(StoredSubHistory & subssh)
1637
if(uniqueKey_ != subssh.uniqueKey_)
1639
LOGERR << "Attempting to add sub-SSH to incorrect SSH";
1643
pair<BinaryData, StoredSubHistory> keyValPair;
1644
keyValPair.first = subssh.hgtX_;
1645
keyValPair.second = subssh;
1646
pair<map<BinaryData, StoredSubHistory>::iterator, bool> insResult;
1647
insResult = subHistMap_.insert(keyValPair);
1649
bool alreadyExisted = !insResult.second;
1652
// If already existed, we need to merge the DB data into the RAM struct
1653
StoredSubHistory & subsshAlreadyInRAM = insResult.first->second;
1654
StoredSubHistory & subsshTriedToAdd = subssh;
1655
LOGINFO << "SubSSH already in SSH...should this happen?";
1656
map<BinaryData, TxIOPair>::iterator iter;
1657
for(iter = subsshTriedToAdd.txioSet_.begin();
1658
iter != subsshTriedToAdd.txioSet_.end();
1661
subsshAlreadyInRAM.txioSet_[iter->first] = iter->second;
1669
////////////////////////////////////////////////////////////////////////////////
1670
// This adds the TxOut if it doesn't exist yet
1671
uint64_t StoredScriptHistory::markTxOutSpent(BinaryData txOutKey8B,
1672
BinaryData txInKey8B)
1674
if(!isInitialized())
1677
if(txOutKey8B.getSize() != 8 || txInKey8B.getSize() != 8)
1679
LOGERR << "Invalid input to mark TxOut spent";
1680
LOGERR << "TxOutKey: '" << txOutKey8B.toHexStr().c_str() << "'";
1681
LOGERR << "TxInKey: '" << txInKey8B.toHexStr().c_str() << "'";
1685
BinaryData first4 = txOutKey8B.getSliceCopy(0,4);
1686
map<BinaryData, StoredSubHistory>::iterator iter;
1687
iter = subHistMap_.find(first4);
1689
if(ITER_NOT_IN_MAP(iter, subHistMap_))
1691
LOGWARN << "Trying to mark TxIO spent, but does not exist!";
1695
uint64_t val = iter->second.markTxOutSpent(txOutKey8B, txInKey8B);
1696
if(val != UINT64_MAX)
1697
totalUnspent_ -= val;
1702
////////////////////////////////////////////////////////////////////////////////
1703
uint64_t StoredScriptHistory::markTxOutUnspent(BinaryData txOutKey8B,
1708
if(!isInitialized())
1711
if(txOutKey8B.getSize() != 8)
1713
LOGERR << "Invalid input to mark TxOut unspent";
1714
LOGERR << "TxOutKey: '" << txOutKey8B.toHexStr().c_str() << "'";
1718
BinaryData first4 = txOutKey8B.getSliceCopy(0,4);
1719
map<BinaryData, StoredSubHistory>::iterator iter;
1720
iter = subHistMap_.find(first4);
1722
if(ITER_NOT_IN_MAP(iter, subHistMap_))
1724
// The SubHistory doesn't actually exist yet, so we have to add it
1725
if(value == UINT64_MAX)
1727
LOGERR << "Tried to create TxOut in SSH but no value supplied!";
1730
pair<BinaryData, StoredSubHistory> toInsert(first4, StoredSubHistory());
1731
iter = subHistMap_.insert(toInsert).first;
1732
iter->second.uniqueKey_ = uniqueKey_;
1733
iter->second.hgtX_ = first4;
1736
// More sanity checking
1737
if(ITER_NOT_IN_MAP(iter, subHistMap_))
1739
LOGERR << "Somehow still don't have the subSSH after trying to insert it";
1743
StoredSubHistory & subssh = iter->second;
1744
uint32_t prevSize = subssh.txioSet_.size();
1745
uint64_t val = subssh.markTxOutUnspent(txOutKey8B, value, isCoinbase, isMultisig);
1746
uint32_t newSize = subssh.txioSet_.size();
1748
// Value returned above is zero if it's multisig, so no need to check here
1749
// Also, markTxOutUnspent doesn't indicate whether a new entry was added,
1750
// so we use txioSet_.size() to update appropriately.
1751
totalUnspent_ += val;
1752
totalTxioCount_ += (newSize - prevSize); // should only ever be +=0 or +=1
1753
useMultipleEntries_ = (totalTxioCount_>1);
1760
////////////////////////////////////////////////////////////////////////////////
1761
bool StoredScriptHistory::haveFullHistoryLoaded(void) const
1763
if(!isInitialized())
1766
uint64_t numTxio = 0;
1767
map<BinaryData, StoredSubHistory>::const_iterator iter;
1768
for(iter = subHistMap_.begin(); iter != subHistMap_.end(); iter++)
1769
numTxio += iter->second.getTxioCount();
1771
if(numTxio > totalTxioCount_)
1772
LOGERR << "Somehow stored total is less than counted total...?";
1774
return (numTxio==totalTxioCount_);
1777
////////////////////////////////////////////////////////////////////////////////
1778
uint64_t StoredScriptHistory::getScriptReceived(bool withMultisig)
1780
if(!haveFullHistoryLoaded())
1784
map<BinaryData, StoredSubHistory>::iterator iter;
1785
for(iter = subHistMap_.begin(); iter != subHistMap_.end(); iter++)
1786
bal += iter->second.getSubHistoryReceived(withMultisig);
1791
////////////////////////////////////////////////////////////////////////////////
1792
uint64_t StoredScriptHistory::getScriptBalance(bool withMultisig)
1794
// If regular balance,
1796
return totalUnspent_;
1798
// If with multisig we have to load and count everything
1799
if(!haveFullHistoryLoaded())
1803
map<BinaryData, StoredSubHistory>::iterator iter;
1804
for(iter = subHistMap_.begin(); iter != subHistMap_.end(); iter++)
1805
bal += iter->second.getSubHistoryBalance(withMultisig);
1810
////////////////////////////////////////////////////////////////////////////////
1811
bool StoredScriptHistory::getFullTxioMap( map<BinaryData, TxIOPair> & mapToFill,
1814
if(!haveFullHistoryLoaded())
1817
map<BinaryData, StoredSubHistory>::iterator iterSubSSH;
1818
for(iterSubSSH = subHistMap_.begin();
1819
iterSubSSH != subHistMap_.end();
1822
StoredSubHistory & subssh = iterSubSSH->second;
1826
// If with multisig, we can just copy everything
1827
mapToFill.insert(subssh.txioSet_.begin(), subssh.txioSet_.end());
1831
// Otherwise, we have to filter out the multisig TxIOs
1832
map<BinaryData, TxIOPair>::iterator iterTxio;
1833
for(iterTxio = subssh.txioSet_.begin();
1834
iterTxio != subssh.txioSet_.end();
1837
if(!iterTxio->second.isMultisig())
1838
mapToFill[iterTxio->first] = iterTxio->second;
1848
////////////////////////////////////////////////////////////////////////////////
1849
// SubSSH object code
1851
// If the SSH has more than one TxIO, then we put them into SubSSH objects,
1852
// which represent a list of TxIOs for the given block. The complexity of
1853
// doing it this way seems unnecessary, but it actually works quite efficiently
1854
// for massively-reused addresses like SatoshiDice.
1855
////////////////////////////////////////////////////////////////////////////////
1856
void StoredSubHistory::unserializeDBValue(BinaryRefReader & brr)
1858
// Get the TxOut list if a pointer was supplied
1859
// This list is unspent-TxOuts only if pruning enabled. You will
1860
// have to dereference each one to check spentness if not pruning
1861
if(hgtX_.getSize() != 4)
1863
LOGERR << "Cannot unserialize DB value until key is set (hgt&dup)";
1864
uniqueKey_.resize(0);
1868
BinaryData fullTxOutKey(8);
1869
hgtX_.copyTo(fullTxOutKey.getPtr());
1871
uint32_t numTxo = (uint32_t)(brr.get_var_int());
1872
for(uint32_t i=0; i<numTxo; i++)
1874
BitUnpacker<uint8_t> bitunpack(brr);
1875
bool isFromSelf = bitunpack.getBit();
1876
bool isCoinbase = bitunpack.getBit();
1877
bool isSpent = bitunpack.getBit();
1878
bool isMulti = bitunpack.getBit();
1880
// We always include the 8-byte value
1881
uint64_t txoValue = brr.get_uint64_t();
1883
// First 4 bytes is same for all TxIOs, and was copied outside the loop.
1884
// So we grab the last four bytes and copy it to the end.
1885
brr.get_BinaryData(fullTxOutKey.getPtr()+4, 4);
1886
TxIOPair txio(fullTxOutKey, txoValue);
1889
txio.setTxIn(brr.get_BinaryDataRef(8));
1891
txio.setTxOutFromSelf(isFromSelf);
1892
txio.setFromCoinbase(isCoinbase);
1893
txio.setMultisig(isMulti);
1898
////////////////////////////////////////////////////////////////////////////////
1899
void StoredSubHistory::serializeDBValue(BinaryWriter & bw ) const
1901
bw.put_var_int(txioSet_.size());
1902
map<BinaryData, TxIOPair>::const_iterator iter;
1903
for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
1905
TxIOPair const & txio = iter->second;
1906
bool isSpent = txio.hasTxInInMain();
1908
// If spent and only maintaining a pruned DB, skip it
1911
if(DBUtils.getDbPruneType()==DB_PRUNE_ALL)
1914
if(!txio.getTxRefOfInput().isInitialized())
1916
LOGERR << "TxIO is spent, but input is not initialized";
1922
BinaryData key8B = txio.getDBKeyOfOutput();
1923
if(!key8B.startsWith(hgtX_))
1924
LOGERR << "How did TxIO key not match hgtX_??";
1926
BitPacker<uint8_t> bitpack;
1927
bitpack.putBit(txio.isTxOutFromSelf());
1928
bitpack.putBit(txio.isFromCoinbase());
1929
bitpack.putBit(txio.hasTxInInMain());
1930
bitpack.putBit(txio.isMultisig());
1931
bw.put_BitPacker(bitpack);
1933
// Always write the value and last 4 bytes of dbkey (first 4 is in dbkey)
1934
bw.put_uint64_t(txio.getValue());
1935
bw.put_BinaryData(key8B.getSliceCopy(4,4));
1937
// If not supposed to write the TxIn, we would've bailed earlier
1939
bw.put_BinaryData(txio.getDBKeyOfInput());
1943
////////////////////////////////////////////////////////////////////////////////
1944
void StoredSubHistory::unserializeDBValue(BinaryData const & bd)
1946
BinaryRefReader brr(bd);
1947
unserializeDBValue(brr);
1950
////////////////////////////////////////////////////////////////////////////////
1951
void StoredSubHistory::unserializeDBValue(BinaryDataRef bdr)
1953
BinaryRefReader brr(bdr);
1954
unserializeDBValue(brr);
1957
////////////////////////////////////////////////////////////////////////////////
1958
BinaryData StoredSubHistory::serializeDBValue(void) const
1961
serializeDBValue(bw);
1962
return bw.getData();
1965
////////////////////////////////////////////////////////////////////////////////
1966
void StoredSubHistory::unserializeDBKey(BinaryDataRef key, bool withPrefix)
1968
uint32_t sz = key.getSize();
1969
BinaryRefReader brr(key);
1974
DBUtils.checkPrefixByte(brr, DB_PREFIX_SCRIPT);
1978
brr.get_BinaryData(uniqueKey_, sz-4);
1979
brr.get_BinaryData(hgtX_, 4);
1982
////////////////////////////////////////////////////////////////////////////////
1983
BinaryData StoredSubHistory::getDBKey(bool withPrefix) const
1987
bw.put_uint8_t(DB_PREFIX_SCRIPT);
1989
bw.put_BinaryData(uniqueKey_);
1990
bw.put_BinaryData(hgtX_);
1991
return bw.getData();
1994
////////////////////////////////////////////////////////////////////////////////
1995
SCRIPT_PREFIX StoredSubHistory::getScriptType(void) const
1997
if(uniqueKey_.getSize() == 0)
1998
return SCRIPT_PREFIX_NONSTD;
2000
return (SCRIPT_PREFIX)uniqueKey_[0];
2003
////////////////////////////////////////////////////////////////////////////////
2004
void StoredSubHistory::pprintFullSubSSH(uint32_t indent)
2006
for(uint32_t ind=0; ind<indent; ind++)
2009
uint32_t hgt = DBUtils.hgtxToHeight(hgtX_);
2010
uint8_t dup = DBUtils.hgtxToDupID(hgtX_);
2011
cout << "SubSSH: " << hgtX_.toHexStr().c_str();
2012
cout << " Hgt&Dup: (" << hgt << "," << (uint32_t)dup << ")" << endl;
2014
// Print all the txioVects
2015
map<BinaryData, TxIOPair>::iterator iter;
2016
for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
2018
for(uint32_t ind=0; ind<indent+3; ind++)
2021
TxIOPair & txio = iter->second;
2025
uint16_t txo = txio.getIndexOfOutput();
2026
BinaryData txoKey = txio.getDBKeyOfOutput();
2027
BinaryRefReader brrTxOut(txoKey);
2028
DBUtils.readBlkDataKeyNoPrefix(brrTxOut, hgt, dup, txi);
2029
cout << "TXIO: (" << hgt << "," << (uint32_t)dup
2030
<< "," << txi << "," << txo << ")";
2032
BinaryData scraddr = txio.getTxOutCopy().getScrAddressStr();
2033
cout << " VALUE: " << (txio.getValue() /COIN);
2034
cout << " isCB: " << (txio.isFromCoinbase() ? "X" : " ");
2035
cout << " isMS: " << (txio.isMultisig() ? "X" : " ");
2036
cout << " Type: " << (uint32_t)uniqueKey_[0];
2037
cout << " Addr: " << uniqueKey_.getSliceCopy(1,4).toHexStr().c_str();
2041
uint16_t txo = txio.getIndexOfInput();
2042
BinaryData txiKey = txio.getDBKeyOfInput();
2043
BinaryRefReader brrTxIn(txiKey);
2044
DBUtils.readBlkDataKeyNoPrefix(brrTxIn, hgt, dup, txi);
2045
cout << " SPENT: (" << hgt << "," << (uint32_t)dup
2046
<< "," << txi << "," << txo << ")";
2054
////////////////////////////////////////////////////////////////////////////////
2055
TxIOPair* StoredSubHistory::findTxio(BinaryData const & dbKey8B, bool withMulti)
2057
map<BinaryData, TxIOPair>::iterator iter = txioSet_.find(dbKey8B);
2058
if(ITER_NOT_IN_MAP(iter, txioSet_))
2062
if(!withMulti && iter->second.isMultisig())
2065
return &(iter->second);
2069
////////////////////////////////////////////////////////////////////////////////
2070
TxIOPair& StoredSubHistory::insertTxio(TxIOPair const & txio, bool withOverwrite)
2072
BinaryData key8B = txio.getDBKeyOfOutput();
2073
if(!key8B.startsWith(hgtX_))
2075
LOGERR << "This txio does not belong in this subSSH";
2076
// Hmm, can't return a NULL ref... let's just fix any bug that causes
2077
// this branch to hit instead of hacking something
2080
pair<BinaryData, TxIOPair> txioInsertPair(txio.getDBKeyOfOutput(), txio);
2081
pair<map<BinaryData, TxIOPair>::iterator, bool> txioInsertResult;
2083
// This returns pair<ExistingOrInsertedIter, wasInserted>
2084
txioInsertResult = txioSet_.insert(txioInsertPair);
2086
// If not inserted, then it was already there. Overwrite if requested
2087
if(!txioInsertResult.second && withOverwrite)
2088
txioInsertResult.first->second = txio;
2090
return txioInsertResult.first->second;
2094
////////////////////////////////////////////////////////////////////////////////
2095
// Since the outer-SSH object tracks total unspent balances, we need to pass
2096
// out the total amount that was deleted from the sub-history, and of course
2097
// return zero if nothing was removed.
2098
uint64_t StoredSubHistory::eraseTxio(TxIOPair const & txio)
2100
return eraseTxio(txio.getDBKeyOfOutput());
2103
////////////////////////////////////////////////////////////////////////////////
2104
uint64_t StoredSubHistory::eraseTxio(BinaryData const & dbKey8B)
2106
map<BinaryData, TxIOPair>::iterator iter = txioSet_.find(dbKey8B);
2107
if(ITER_NOT_IN_MAP(iter, txioSet_))
2111
TxIOPair & txio = iter->second;
2112
uint64_t valueRemoved = txio.getValue();
2113
if(txio.hasTxInInMain() || txio.isMultisig())
2116
txioSet_.erase(iter);
2117
return valueRemoved;
2122
////////////////////////////////////////////////////////////////////////////////
2123
uint64_t StoredSubHistory::getSubHistoryReceived(bool withMultisig)
2126
map<BinaryData, TxIOPair>::iterator iter;
2127
for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
2128
if(!iter->second.isMultisig() || withMultisig)
2129
bal += iter->second.getValue();
2134
////////////////////////////////////////////////////////////////////////////////
2135
uint64_t StoredSubHistory::getSubHistoryBalance(bool withMultisig)
2138
map<BinaryData, TxIOPair>::iterator iter;
2139
for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
2141
if(!iter->second.hasTxIn())
2142
if(!iter->second.isMultisig() || withMultisig)
2143
bal += iter->second.getValue();
2148
////////////////////////////////////////////////////////////////////////////////
2149
uint64_t StoredSubHistory::markTxOutSpent(BinaryData txOutKey8B,
2150
BinaryData txInKey8B)
2152
// We found the TxIO we care about
2153
if(DBUtils.getDbPruneType() != DB_PRUNE_NONE)
2155
LOGERR << "Have not yet implemented pruning logic yet!";
2159
TxIOPair * txioptr = findTxio(txOutKey8B);
2162
LOGERR << "We should've found an STXO in the SSH but didn't";
2166
if(txioptr->hasTxInInMain())
2168
LOGWARN << "TxOut is already marked as spent";
2172
if(txInKey8B.getSize() != 8)
2174
LOGERR << "TxIn key input not valid! " << txInKey8B.toHexStr();
2178
txioptr->setTxIn(txInKey8B);
2180
// Return value spent only if not multisig
2181
return (txioptr->isMultisig() ? 0 : txioptr->getValue());
2186
////////////////////////////////////////////////////////////////////////////////
2187
// This method will add the TxIOPair to the SSH object if it doesn't exist,
2188
// in addition to marking it unspent.
2190
// If there is a 2-of-3 multisig scraddr M, which includes pubkeys, X, Y and Z,
2191
// then the DB will ultimately look like this:
2195
// PREFIX_SCRIPT + M TxIO.isMultisigRef == false
2196
// PREFIX_SCRIPT + X TxIO.isMultisigRef == true
2197
// PREFIX_SCRIPT + Y TxIO.isMultisigRef == true
2198
// PREFIX_SCRIPT + Z TxIO.isMultisigRef == true
2200
// We will ultimately update all four entries, but the TxIOs that are added
2201
// to X, Y and Z will be flagged so they are not interpretted as single-sig
2202
// addresses. We do this so we can later lookup multi-sig addresses that
2203
// involve a given scraddr, but don't automatically include them in any
2204
// balance or UTXO set calculations.
2206
// Returns the difference to be applied to totalUnspent_ in the outer SSH
2207
// (unless it's UINT64_MAX which is interpretted as failure)
2208
uint64_t StoredSubHistory::markTxOutUnspent(BinaryData txOutKey8B,
2213
TxIOPair* txioptr = findTxio(txOutKey8B);
2216
if(DBUtils.getDbPruneType() != DB_PRUNE_NONE)
2218
LOGERR << "Found STXO that we expected to already be pruned...";
2222
if(!txioptr->hasTxInInMain())
2224
LOGWARN << "STXO already marked unspent in SSH";
2228
txioptr->setTxIn(TxRef(), UINT32_MAX);
2229
return (txioptr->isMultisig() ? 0 : txioptr->getValue());
2233
if(value==UINT64_MAX)
2235
LOGERR << "Need to add TxOut to sub-history, but no value supplied!";
2239
// The TxIOPair was not in the SSH yet; add it
2240
TxIOPair txio = TxIOPair(txOutKey8B, value);
2241
txio.setFromCoinbase(isCoinbase);
2242
txio.setMultisig(isMultisigRef);
2243
txio.setTxOutFromSelf(false); // in super-node mode, we don't use this
2245
return (isMultisigRef ? 0 : value);
2249
/* Meh, no demand for this functionality yet ... finish it later
2250
////////////////////////////////////////////////////////////////////////////////
2251
// One loop to compute all four values
2252
// values[0] ~ current balance no multisig
2253
// values[1] ~ current balance multisig-only
2254
// values[2] ~ total received no multisig
2255
// values[3] ~ total received multisig-only
2256
vector<uint64_t> StoredSubHistory::getSubHistoryValues(void)
2259
vector<uint64_t> values(4);
2260
for(uint32_t i=0; i<4; i++)
2263
map<BinaryData, TxIOPair>::iterator iter;
2264
for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
2266
if(iter->second.isMultisig())
2270
if(!iter->second.hasTxIn())
2271
if(!iter->second.isMultisig() || withMultisig)
2272
bal += iter->second.getValue();
2278
////////////////////////////////////////////////////////////////////////////////
2279
////////////////////////////////////////////////////////////////////////////////
2280
void StoredUndoData::unserializeDBValue(BinaryRefReader & brr)
2282
brr.get_BinaryData(blockHash_, 32);
2284
uint32_t nStxoRmd = brr.get_uint32_t();
2285
stxOutsRemovedByBlock_.clear();
2286
stxOutsRemovedByBlock_.resize(nStxoRmd);
2288
for(uint32_t i=0; i<nStxoRmd; i++)
2290
StoredTxOut & stxo = stxOutsRemovedByBlock_[i];
2292
// Store the standard flags that go with StoredTxOuts, minus spentness
2293
BitUnpacker<uint8_t> bitunpack(brr);
2294
stxo.unserDbType_ = bitunpack.getBits(4);
2295
stxo.txVersion_ = bitunpack.getBits(2);
2296
stxo.isCoinbase_ = bitunpack.getBit();
2298
BinaryData hgtx = brr.get_BinaryData(4);
2299
stxo.blockHeight_ = DBUtils.hgtxToHeight(hgtx);
2300
stxo.duplicateID_ = DBUtils.hgtxToDupID(hgtx);
2301
stxo.txIndex_ = brr.get_uint16_t(BIGENDIAN);
2302
stxo.txOutIndex_ = brr.get_uint16_t(BIGENDIAN);
2304
// This is the raw OutPoint of the removed TxOut. May not strictly
2305
// be necessary for processing undo ops in this DB (but might be),
2306
// but may be useful for giving to peers if needed w/o exta lookups
2307
brr.get_BinaryData(stxo.parentHash_, 32);
2308
stxo.txOutIndex_ = brr.get_uint32_t();
2310
// Then read the raw TxOut itself
2311
stxo.unserialize(brr);
2314
uint32_t nOpAdded = brr.get_uint32_t();
2315
outPointsAddedByBlock_.clear();
2316
outPointsAddedByBlock_.resize(nStxoRmd);
2317
for(uint32_t i=0; i<nOpAdded; i++)
2318
outPointsAddedByBlock_[i].unserialize(brr);
2322
////////////////////////////////////////////////////////////////////////////////
2323
void StoredUndoData::serializeDBValue(BinaryWriter & bw ) const
2325
bw.put_BinaryData(blockHash_);
2327
uint32_t nStxoRmd = stxOutsRemovedByBlock_.size();
2328
uint32_t nOpAdded = outPointsAddedByBlock_.size();
2331
// Store the full TxOuts that were removed... since they will have been
2332
// removed from the DB and have to be fully added again if we undo the block
2333
bw.put_uint32_t(nStxoRmd);
2334
for(uint32_t i=0; i<nStxoRmd; i++)
2336
StoredTxOut const & stxo = stxOutsRemovedByBlock_[i];
2338
if(stxo.parentHash_.getSize() == 0 ||
2339
stxo.txOutIndex_ == UINT16_MAX )
2341
LOGERR << "Can't write undo data w/o parent hash and/or TxOut index";
2345
// Store the standard flags that go with StoredTxOuts, minus spentness
2346
BitPacker<uint8_t> bitpack;
2347
bitpack.putBits( (uint8_t)DBUtils.getArmoryDbType(), 4);
2348
bitpack.putBits( (uint8_t)stxo.txVersion_, 2);
2349
bitpack.putBit( stxo.isCoinbase_);
2351
bw.put_BitPacker(bitpack);
2353
// Put the blkdata key directly into the DB to save us a lookup
2354
bw.put_BinaryData( DBUtils.getBlkDataKeyNoPrefix( stxo.blockHeight_,
2359
bw.put_BinaryData(stxo.parentHash_);
2360
bw.put_uint32_t((uint32_t)stxo.txOutIndex_);
2361
bw.put_BinaryData(stxo.getSerializedTxOut());
2364
// Store just the OutPoints of the TxOuts that were added by this block.
2365
// If we're undoing this block, we have the full TxOuts already in the DB
2366
// under the block key with same hgt & dup. We could probably avoid
2367
// storing this data at all due to this DB design, but it is needed if we
2368
// are ever going to serve any other process that expects the OutPoint list.
2369
bw.put_uint32_t(nOpAdded);
2370
for(uint32_t i=0; i<nOpAdded; i++)
2371
bw.put_BinaryData(outPointsAddedByBlock_[i].serialize());
2374
////////////////////////////////////////////////////////////////////////////////
2375
void StoredUndoData::unserializeDBValue(BinaryData const & bd)
2377
BinaryRefReader brr(bd);
2378
unserializeDBValue(brr);
2381
////////////////////////////////////////////////////////////////////////////////
2382
void StoredUndoData::unserializeDBValue(BinaryDataRef bdr)
2385
BinaryRefReader brr(bdr);
2386
unserializeDBValue(brr);
2389
////////////////////////////////////////////////////////////////////////////////
2390
BinaryData StoredUndoData::serializeDBValue(void) const
2393
serializeDBValue(bw);
2394
return bw.getData();
2397
////////////////////////////////////////////////////////////////////////////////
2398
BinaryData StoredUndoData::getDBKey(bool withPrefix) const
2401
return DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_);
2405
bw.put_uint8_t((uint8_t)DB_PREFIX_UNDODATA);
2406
bw.put_BinaryData( DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_));
2407
return bw.getData();
2412
////////////////////////////////////////////////////////////////////////////////
2413
////////////////////////////////////////////////////////////////////////////////
2414
void StoredTxHints::unserializeDBValue(BinaryRefReader & brr)
2416
uint64_t numHints = (brr.getSizeRemaining()==0 ? 0 : brr.get_var_int());
2417
dbKeyList_.resize((uint32_t)numHints);
2418
for(uint32_t i=0; i<numHints; i++)
2419
brr.get_BinaryData(dbKeyList_[i], 6);
2421
// Preferred simply means it's supposed to be first in the list
2422
// This simply improves search time in the event there's multiple hints
2424
preferredDBKey_ = dbKeyList_[0];
2428
////////////////////////////////////////////////////////////////////////////////
2429
void StoredTxHints::serializeDBValue(BinaryWriter & bw ) const
2431
bw.put_var_int(dbKeyList_.size());
2433
// Find and write the preferred key first, skip all unpreferred (the first
2434
// one in the list is the preferred key... that paradigm could be improved
2436
for(uint32_t i=0; i<dbKeyList_.size(); i++)
2438
if(dbKeyList_[i] != preferredDBKey_)
2441
bw.put_BinaryData(dbKeyList_[i]);
2445
// Now write all the remaining keys in whatever order they are naturally
2446
// sorted (skip the preferred key since it was already written)
2447
for(uint32_t i=0; i<dbKeyList_.size(); i++)
2449
if(dbKeyList_[i] == preferredDBKey_)
2452
bw.put_BinaryData(dbKeyList_[i]);
2456
////////////////////////////////////////////////////////////////////////////////
2457
void StoredTxHints::unserializeDBValue(BinaryData const & bd)
2459
BinaryRefReader brr(bd);
2460
unserializeDBValue(brr);
2463
////////////////////////////////////////////////////////////////////////////////
2464
void StoredTxHints::unserializeDBValue(BinaryDataRef bdr)
2467
BinaryRefReader brr(bdr);
2468
unserializeDBValue(brr);
2471
////////////////////////////////////////////////////////////////////////////////
2472
BinaryData StoredTxHints::serializeDBValue(void) const
2475
serializeDBValue(bw);
2476
return bw.getData();
2480
////////////////////////////////////////////////////////////////////////////////
2481
BinaryData StoredTxHints::getDBKey(bool withPrefix) const
2484
return txHashPrefix_;
2488
bw.put_uint8_t((uint8_t)DB_PREFIX_TXHINTS);
2489
bw.put_BinaryData( txHashPrefix_);
2490
return bw.getData();
2494
////////////////////////////////////////////////////////////////////////////////
2495
void StoredTxHints::unserializeDBKey(BinaryDataRef key, bool withPrefix)
2498
txHashPrefix_ = key.getSliceCopy(1, 4);
2500
txHashPrefix_ = key;
2503
////////////////////////////////////////////////////////////////////////////////
2504
////////////////////////////////////////////////////////////////////////////////
2505
void StoredHeadHgtList::unserializeDBValue(BinaryRefReader & brr)
2507
uint32_t numHeads = brr.get_uint8_t();
2508
dupAndHashList_.resize(numHeads);
2509
preferredDup_ = UINT8_MAX;
2510
for(uint32_t i=0; i<numHeads; i++)
2512
uint8_t dup = brr.get_uint8_t();
2513
dupAndHashList_[i].first = dup & 0x7f;
2514
brr.get_BinaryData(dupAndHashList_[i].second, 32);
2515
if((dup & 0x80) > 0)
2516
preferredDup_ = dup & 0x7f;
2520
////////////////////////////////////////////////////////////////////////////////
2521
void StoredHeadHgtList::serializeDBValue(BinaryWriter & bw ) const
2523
bw.put_uint8_t( dupAndHashList_.size() );
2525
// Write the preferred/valid block header first
2526
for(uint32_t i=0; i<dupAndHashList_.size(); i++)
2528
if(dupAndHashList_[i].first != preferredDup_)
2531
bw.put_uint8_t(dupAndHashList_[i].first | 0x80);
2532
bw.put_BinaryData(dupAndHashList_[i].second);
2536
// Now write everything else
2537
for(uint32_t i=0; i<dupAndHashList_.size(); i++)
2539
if(dupAndHashList_[i].first == preferredDup_)
2542
bw.put_uint8_t(dupAndHashList_[i].first & 0x7f);
2543
bw.put_BinaryData(dupAndHashList_[i].second);
2548
////////////////////////////////////////////////////////////////////////////////
2549
void StoredHeadHgtList::unserializeDBValue(BinaryData const & bd)
2551
BinaryRefReader brr(bd);
2552
unserializeDBValue(brr);
2555
////////////////////////////////////////////////////////////////////////////////
2556
void StoredHeadHgtList::unserializeDBValue(BinaryDataRef bdr)
2559
BinaryRefReader brr(bdr);
2560
unserializeDBValue(brr);
2563
////////////////////////////////////////////////////////////////////////////////
2564
BinaryData StoredHeadHgtList::serializeDBValue(void) const
2567
serializeDBValue(bw);
2568
return bw.getData();
2571
////////////////////////////////////////////////////////////////////////////////
2572
BinaryData StoredHeadHgtList::getDBKey(bool withPrefix) const
2576
bw.put_uint8_t((uint8_t)DB_PREFIX_HEADHGT);
2577
bw.put_uint32_t(height_, BIGENDIAN);
2578
return bw.getData();
2582
////////////////////////////////////////////////////////////////////////////////
2583
void StoredHeadHgtList::unserializeDBKey(BinaryDataRef key)
2585
BinaryRefReader brr(key);
2586
if(key.getSize() == 5)
2588
uint8_t prefix = brr.get_uint8_t();
2589
if(prefix != DB_PREFIX_HEADHGT)
2591
LOGERR << "Unserialized HEADHGT key but wrong prefix";
2596
height_ = brr.get_uint32_t(BIGENDIAN);
2600
////////////////////////////////////////////////////////////////////////////////
2601
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKey( BinaryRefReader & brr,
2606
uint16_t tempTxOutIdx;
2607
return readBlkDataKey(brr, height, dupID, tempTxIdx, tempTxOutIdx);
2610
////////////////////////////////////////////////////////////////////////////////
2611
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKey( BinaryRefReader & brr,
2616
uint16_t tempTxOutIdx;
2617
return readBlkDataKey(brr, height, dupID, txIdx, tempTxOutIdx);
2620
////////////////////////////////////////////////////////////////////////////////
2621
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKey( BinaryRefReader & brr,
2625
uint16_t & txOutIdx)
2627
uint8_t prefix = brr.get_uint8_t();
2628
if(prefix != (uint8_t)DB_PREFIX_TXDATA)
2630
height = 0xffffffff;
2637
return readBlkDataKeyNoPrefix(brr, height, dupID, txIdx, txOutIdx);
2640
////////////////////////////////////////////////////////////////////////////////
2641
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKeyNoPrefix(
2642
BinaryRefReader & brr,
2647
uint16_t tempTxOutIdx;
2648
return readBlkDataKeyNoPrefix(brr, height, dupID, tempTxIdx, tempTxOutIdx);
2651
////////////////////////////////////////////////////////////////////////////////
2652
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKeyNoPrefix(
2653
BinaryRefReader & brr,
2658
uint16_t tempTxOutIdx;
2659
return readBlkDataKeyNoPrefix(brr, height, dupID, txIdx, tempTxOutIdx);
2662
////////////////////////////////////////////////////////////////////////////////
2663
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKeyNoPrefix(
2664
BinaryRefReader & brr,
2668
uint16_t & txOutIdx)
2670
BinaryData hgtx = brr.get_BinaryData(4);
2671
height = hgtxToHeight(hgtx);
2672
dupID = hgtxToDupID(hgtx);
2674
if(brr.getSizeRemaining() == 0)
2678
return BLKDATA_HEADER;
2680
else if(brr.getSizeRemaining() == 2)
2682
txIdx = brr.get_uint16_t(BIGENDIAN);
2686
else if(brr.getSizeRemaining() == 4)
2688
txIdx = brr.get_uint16_t(BIGENDIAN);
2689
txOutIdx = brr.get_uint16_t(BIGENDIAN);
2690
return BLKDATA_TXOUT;
2694
LOGERR << "Unexpected bytes remaining: " << brr.getSizeRemaining();
2703
////////////////////////////////////////////////////////////////////////////////
2704
////////////////////////////////////////////////////////////////////////////////
2705
string GlobalDBUtilities::getPrefixName(uint8_t prefixInt)
2707
return getPrefixName((DB_PREFIX)prefixInt);
2710
////////////////////////////////////////////////////////////////////////////////
2711
string GlobalDBUtilities::getPrefixName(DB_PREFIX pref)
2715
case DB_PREFIX_DBINFO: return string("DBINFO");
2716
case DB_PREFIX_TXDATA: return string("TXDATA");
2717
case DB_PREFIX_SCRIPT: return string("SCRIPT");
2718
case DB_PREFIX_TXHINTS: return string("TXHINTS");
2719
case DB_PREFIX_TRIENODES: return string("TRIENODES");
2720
case DB_PREFIX_HEADHASH: return string("HEADHASH");
2721
case DB_PREFIX_HEADHGT: return string("HEADHGT");
2722
case DB_PREFIX_UNDODATA: return string("UNDODATA");
2723
default: return string("<unknown>");
2727
/////////////////////////////////////////////////////////////////////////////
2728
bool GlobalDBUtilities::checkPrefixByteWError( BinaryRefReader & brr,
2730
bool rewindWhenDone)
2732
uint8_t oneByte = brr.get_uint8_t();
2734
if(oneByte == (uint8_t)prefix)
2738
LOGERR << "Unexpected prefix byte: "
2739
<< "Expected: " << getPrefixName(prefix)
2740
<< "Received: " << getPrefixName(oneByte);
2750
/////////////////////////////////////////////////////////////////////////////
2751
bool GlobalDBUtilities::checkPrefixByte( BinaryRefReader & brr,
2753
bool rewindWhenDone)
2755
uint8_t oneByte = brr.get_uint8_t();
2756
bool out = (oneByte == (uint8_t)prefix);
2764
/////////////////////////////////////////////////////////////////////////////
2765
BinaryData GlobalDBUtilities::getBlkDataKey( uint32_t height,
2769
bw.put_uint8_t( DB_PREFIX_TXDATA );
2770
bw.put_BinaryData( heightAndDupToHgtx(height,dup) );
2771
return bw.getData();
2774
/////////////////////////////////////////////////////////////////////////////
2775
BinaryData GlobalDBUtilities::getBlkDataKey( uint32_t height,
2780
bw.put_uint8_t( DB_PREFIX_TXDATA );
2781
bw.put_BinaryData( heightAndDupToHgtx(height,dup) );
2782
bw.put_uint16_t( txIdx, BIGENDIAN);
2783
return bw.getData();
2786
/////////////////////////////////////////////////////////////////////////////
2787
BinaryData GlobalDBUtilities::getBlkDataKey(uint32_t height,
2793
bw.put_uint8_t( DB_PREFIX_TXDATA );
2794
bw.put_BinaryData( heightAndDupToHgtx(height,dup) );
2795
bw.put_uint16_t( txIdx, BIGENDIAN);
2796
bw.put_uint16_t( txOutIdx, BIGENDIAN);
2797
return bw.getData();
2800
/////////////////////////////////////////////////////////////////////////////
2801
BinaryData GlobalDBUtilities::getBlkDataKeyNoPrefix( uint32_t height,
2804
return heightAndDupToHgtx(height,dup);
2807
/////////////////////////////////////////////////////////////////////////////
2808
BinaryData GlobalDBUtilities::getBlkDataKeyNoPrefix( uint32_t height,
2813
bw.put_BinaryData( heightAndDupToHgtx(height,dup));
2814
bw.put_uint16_t( txIdx, BIGENDIAN);
2815
return bw.getData();
2818
/////////////////////////////////////////////////////////////////////////////
2819
BinaryData GlobalDBUtilities::getBlkDataKeyNoPrefix( uint32_t height,
2825
bw.put_BinaryData( heightAndDupToHgtx(height,dup));
2826
bw.put_uint16_t( txIdx, BIGENDIAN);
2827
bw.put_uint16_t( txOutIdx, BIGENDIAN);
2828
return bw.getData();
2831
/////////////////////////////////////////////////////////////////////////////
2832
uint32_t GlobalDBUtilities::hgtxToHeight(const BinaryData& hgtx)
2834
return (READ_UINT32_BE(hgtx) >> 8);
2838
/////////////////////////////////////////////////////////////////////////////
2839
uint8_t GlobalDBUtilities::hgtxToDupID(const BinaryData& hgtx)
2841
return (READ_UINT32_BE(hgtx) & 0x7f);
2844
/////////////////////////////////////////////////////////////////////////////
2845
BinaryData GlobalDBUtilities::heightAndDupToHgtx(uint32_t hgt, uint8_t dup)
2847
uint32_t hgtxInt = (hgt<<8) | (uint32_t)dup;
2848
return WRITE_UINT32_BE(hgtxInt);
2851
// kate: indent-width 3; replace-tabs on;