~ubuntu-branches/debian/jessie/armory/jessie

« back to all changes in this revision

Viewing changes to cppForSwig/StoredBlockObj.cpp

  • Committer: Package Import Robot
  • Author(s): Joseph Bisch
  • Date: 2014-10-07 10:22:45 UTC
  • Revision ID: package-import@ubuntu.com-20141007102245-2s3x3rhjxg689hek
Tags: upstream-0.92.3
ImportĀ upstreamĀ versionĀ 0.92.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
////////////////////////////////////////////////////////////////////////////////
 
2
//                                                                            //
 
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                      //
 
6
//                                                                            //
 
7
////////////////////////////////////////////////////////////////////////////////
 
8
#include <vector>
 
9
#include <list>
 
10
#include <map>
 
11
#include "StoredBlockObj.h"
 
12
 
 
13
DB_PRUNE_TYPE  GlobalDBUtilities::dbPruneType_  = DB_PRUNE_WHATEVER;
 
14
ARMORY_DB_TYPE GlobalDBUtilities::armoryDbType_ = ARMORY_DB_WHATEVER;
 
15
GlobalDBUtilities* GlobalDBUtilities::theOneUtilsObj_ = NULL;
 
16
 
 
17
/////////////////////////////////////////////////////////////////////////////
 
18
BinaryData StoredDBInfo::getDBKey(void)
 
19
{
 
20
   // Return a key that is guaranteed to be before all other non-empty
 
21
   // DB keys
 
22
   static BinaryData dbinfokey(0);
 
23
   if(dbinfokey.getSize() == 0)
 
24
   {
 
25
      BinaryWriter bw(1);
 
26
      bw.put_uint8_t((uint8_t)DB_PREFIX_DBINFO); 
 
27
      dbinfokey = bw.getData();
 
28
   }
 
29
   return dbinfokey;
 
30
}
 
31
 
 
32
/////////////////////////////////////////////////////////////////////////////
 
33
void StoredDBInfo::unserializeDBValue(BinaryRefReader & brr)
 
34
{
 
35
   if(brr.getSizeRemaining() < 44)
 
36
   {
 
37
      magic_.resize(0);
 
38
      topBlkHgt_ = UINT32_MAX;
 
39
      topBlkHash_.resize(0);
 
40
      return;
 
41
   }
 
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);
 
47
 
 
48
   armoryVer_  =                 bitunpack.getBits(4);
 
49
   armoryType_ = (ARMORY_DB_TYPE)bitunpack.getBits(4);
 
50
   pruneType_  = (DB_PRUNE_TYPE) bitunpack.getBits(4);
 
51
}
 
52
 
 
53
/////////////////////////////////////////////////////////////////////////////
 
54
void StoredDBInfo::serializeDBValue(BinaryWriter & bw ) const
 
55
{
 
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);
 
60
 
 
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_);
 
66
}
 
67
 
 
68
////////////////////////////////////////////////////////////////////////////////
 
69
void StoredDBInfo::unserializeDBValue(BinaryData const & bd)
 
70
{
 
71
   BinaryRefReader brr(bd);
 
72
   unserializeDBValue(brr);
 
73
}
 
74
 
 
75
////////////////////////////////////////////////////////////////////////////////
 
76
void StoredDBInfo::unserializeDBValue(BinaryDataRef bdr)
 
77
                                  
 
78
{
 
79
   BinaryRefReader brr(bdr);
 
80
   unserializeDBValue(brr);
 
81
}
 
82
 
 
83
////////////////////////////////////////////////////////////////////////////////
 
84
BinaryData StoredDBInfo::serializeDBValue(void) const
 
85
{
 
86
   BinaryWriter bw;
 
87
   serializeDBValue(bw);
 
88
   return bw.getData();
 
89
}
 
90
 
 
91
/////////////////////////////////////////////////////////////////////////////
 
92
void StoredDBInfo::pprintOneLine(uint32_t indent)
 
93
{
 
94
   for(uint32_t i=0; i<indent; i++)
 
95
      cout << " ";
 
96
   
 
97
   cout << "DBINFO: " 
 
98
        << " TopBlk: " << topBlkHgt_
 
99
        << " , " << topBlkHash_.getSliceCopy(0,4).toHexStr().c_str()
 
100
        << endl;
 
101
}
 
102
 
 
103
/////////////////////////////////////////////////////////////////////////////
 
104
void StoredHeader::setKeyData(uint32_t hgt, uint8_t dupID)
 
105
{
 
106
   // Set the params for this SBH object
 
107
   blockHeight_  = hgt;
 
108
   duplicateID_  = dupID;
 
109
 
 
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);
 
114
}
 
115
 
 
116
/////////////////////////////////////////////////////////////////////////////
 
117
void StoredHeader::setHeightAndDup(uint32_t hgt, uint8_t dupID)
 
118
{
 
119
   blockHeight_ = hgt;
 
120
   duplicateID_ = dupID;
 
121
}
 
122
 
 
123
/////////////////////////////////////////////////////////////////////////////
 
124
void StoredHeader::setHeightAndDup(BinaryData hgtx)
 
125
{
 
126
   blockHeight_ = DBUtils.hgtxToHeight(hgtx);
 
127
   duplicateID_ = DBUtils.hgtxToDupID(hgtx);
 
128
}
 
129
 
 
130
/////////////////////////////////////////////////////////////////////////////
 
131
bool StoredHeader::haveFullBlock(void) const
 
132
{
 
133
   if(dataCopy_.getSize() != HEADER_SIZE)
 
134
      return false;
 
135
 
 
136
   for(uint16_t tx=0; tx<numTx_; tx++)
 
137
   {
 
138
      map<uint16_t, StoredTx>::const_iterator iter = stxMap_.find(tx);
 
139
      if(ITER_NOT_IN_MAP(iter, stxMap_))
 
140
         return false;
 
141
      if(!iter->second.haveAllTxOut())
 
142
         return false;
 
143
   }
 
144
 
 
145
   return true;
 
146
}
 
147
 
 
148
/////////////////////////////////////////////////////////////////////////////
 
149
BinaryData StoredHeader::getSerializedBlock(void) const
 
150
{
 
151
   if(!haveFullBlock())
 
152
      return BinaryData(0);
 
153
 
 
154
   BinaryWriter bw;
 
155
   if(numBytes_>0)
 
156
      bw.reserve(numBytes_+100); // add extra room for header and var_int
 
157
 
 
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());
 
162
   
 
163
   return bw.getData();
 
164
}
 
165
 
 
166
 
 
167
/////////////////////////////////////////////////////////////////////////////
 
168
BinaryData StoredHeader::getDBKey(bool withPrefix) const
 
169
{
 
170
   if(blockHeight_==UINT32_MAX || duplicateID_==UINT8_MAX)
 
171
   {
 
172
      LOGERR << "Requesting DB key for incomplete SBH";
 
173
      return BinaryData(0);
 
174
   }
 
175
 
 
176
   if(withPrefix)
 
177
      return DBUtils.getBlkDataKey(blockHeight_, duplicateID_);
 
178
   else
 
179
      return DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_);
 
180
 
 
181
}
 
182
 
 
183
 
 
184
 
 
185
/////////////////////////////////////////////////////////////////////////////
 
186
void StoredHeader::createFromBlockHeader(BlockHeader & bh)
 
187
{
 
188
   if(!bh.isInitialized())
 
189
   {
 
190
      LOGERR << "trying to create from uninitialized block header";
 
191
      return;
 
192
   } 
 
193
 
 
194
   unserialize(bh.serialize());
 
195
 
 
196
   numTx_ = bh.getNumTx();
 
197
   numBytes_ = bh.getBlockSize();
 
198
   blockHeight_ = bh.getBlockHeight();
 
199
   duplicateID_ = UINT8_MAX;
 
200
   isMainBranch_ = bh.isMainBranch();
 
201
   hasBlockHeader_ = true;
 
202
}
 
203
 
 
204
////////////////////////////////////////////////////////////////////////////////
 
205
Tx StoredHeader::getTxCopy(uint16_t i)
 
206
 
207
   if(KEY_IN_MAP(i, stxMap_))
 
208
      return stxMap_[i].getTxCopy();
 
209
   else
 
210
      return Tx();
 
211
}
 
212
 
 
213
////////////////////////////////////////////////////////////////////////////////
 
214
BinaryData StoredHeader::getSerializedTx(uint16_t i)
 
215
 
216
   if(KEY_IN_MAP(i, stxMap_))
 
217
      return stxMap_[i].getSerializedTx();
 
218
   else
 
219
      return BinaryData(0);
 
220
}
 
221
 
 
222
/////////////////////////////////////////////////////////////////////////////
 
223
void StoredHeader::unserialize(BinaryData const & header80B)
 
224
{
 
225
   unserialize(header80B.getRef());
 
226
}
 
227
 
 
228
/////////////////////////////////////////////////////////////////////////////
 
229
void StoredHeader::unserialize(BinaryRefReader brr)
 
230
{
 
231
   unserialize(brr.getRawRef());
 
232
}
 
233
 
 
234
/////////////////////////////////////////////////////////////////////////////
 
235
void StoredHeader::unserialize(BinaryDataRef header80B)
 
236
{
 
237
   if(header80B.getSize() != HEADER_SIZE)
 
238
   {
 
239
      LOGERR << "Asked to unserialize a non-80-byte header";
 
240
      return;
 
241
   }
 
242
   dataCopy_.copyFrom(header80B);
 
243
   BtcUtils::getHash256(header80B, thisHash_);
 
244
}
 
245
 
 
246
/////////////////////////////////////////////////////////////////////////////
 
247
void StoredHeader::unserializeFullBlock(BinaryRefReader brr, 
 
248
                                        bool doFrag,
 
249
                                        bool withPrefix)
 
250
{
 
251
   if(withPrefix)
 
252
   {
 
253
      BinaryData magic  = brr.get_BinaryData(4);
 
254
      uint32_t   nBytes = brr.get_uint32_t();
 
255
 
 
256
      if(brr.getSizeRemaining() < nBytes)
 
257
      {
 
258
         LOGERR << "Not enough bytes remaining in BRR to read block";
 
259
         return;
 
260
      }
 
261
   }
 
262
 
 
263
   BlockHeader bh(brr); 
 
264
   uint32_t nTx = (uint32_t)brr.get_var_int();
 
265
 
 
266
   createFromBlockHeader(bh);
 
267
   numTx_ = nTx;
 
268
   
 
269
   numBytes_ = HEADER_SIZE + BtcUtils::calcVarIntSize(numTx_);
 
270
   if(dataCopy_.getSize() != HEADER_SIZE)
 
271
   {
 
272
      LOGERR << "Unserializing header did not produce 80-byte object!";
 
273
      return;
 
274
   }
 
275
 
 
276
   BtcUtils::getHash256(dataCopy_, thisHash_);
 
277
 
 
278
   for(uint32_t tx=0; tx<nTx; tx++)
 
279
   {
 
280
      // We're going to have to come back to the beginning of the tx, later
 
281
      uint32_t txStart = brr.getPosition();
 
282
 
 
283
      // Read a regular tx and then convert it
 
284
      Tx thisTx(brr);
 
285
      numBytes_ += thisTx.getSize();
 
286
 
 
287
      // Now add it to the map
 
288
      stxMap_[tx] = StoredTx();
 
289
      StoredTx & stx = stxMap_[tx];
 
290
 
 
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();
 
295
      stx.txIndex_       = tx;
 
296
 
 
297
 
 
298
      // Regardless of whether the tx is fragged, we still need the STXO map
 
299
      // to be updated and consistent
 
300
      brr.resetPosition();
 
301
      brr.advance(txStart + thisTx.getTxOutOffset(0));
 
302
      for(uint32_t txo=0; txo < thisTx.getNumTxOut(); txo++)
 
303
      {
 
304
         stx.stxoMap_[txo] = StoredTxOut();
 
305
         StoredTxOut & stxo = stx.stxoMap_[txo];
 
306
 
 
307
         stxo.unserialize(brr);
 
308
         stxo.txVersion_      = thisTx.getVersion();
 
309
         stxo.blockHeight_    = UINT32_MAX;
 
310
         stxo.duplicateID_     = UINT8_MAX;
 
311
         stxo.txIndex_        = tx;
 
312
         stxo.txOutIndex_     = txo;
 
313
         stxo.isCoinbase_     = thisTx.getTxInCopy(0).isCoinbase();
 
314
      }
 
315
 
 
316
      // Sitting at the nLockTime, 4 bytes before the end
 
317
      brr.advance(4);
 
318
 
 
319
      // Finally, add the 
 
320
      stxMap_[tx] = stx;
 
321
   }
 
322
}
 
323
 
 
324
/////////////////////////////////////////////////////////////////////////////
 
325
void StoredHeader::unserializeFullBlock(BinaryDataRef block, 
 
326
                                        bool doFrag,
 
327
                                        bool withPrefix)
 
328
{
 
329
   BinaryRefReader brr(block);
 
330
   unserializeFullBlock(brr, doFrag, withPrefix);
 
331
}
 
332
 
 
333
/////////////////////////////////////////////////////////////////////////////
 
334
bool StoredHeader::serializeFullBlock(BinaryWriter & bw) const
 
335
{
 
336
   if(!haveFullBlock())
 
337
   {
 
338
      LOGERR << "Attempted to serialize full block, but only have partial";
 
339
      return false;
 
340
   }
 
341
 
 
342
   if(numTx_ == UINT32_MAX)
 
343
   {
 
344
      LOGERR << "Number of tx not available while serializing full block";
 
345
      return false;
 
346
   }
 
347
 
 
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++)
 
353
   {
 
354
      if(!iter->second.haveAllTxOut())
 
355
      {
 
356
         LOGERR << "Don't have all TxOut in tx during serialize full block";
 
357
         return false;
 
358
      }
 
359
      bwTemp.put_BinaryData(iter->second.getSerializedTx());
 
360
   }
 
361
   
 
362
   bw.put_BinaryData(bwTemp.getDataRef());
 
363
   return true;
 
364
}
 
365
 
 
366
/////////////////////////////////////////////////////////////////////////////
 
367
void StoredHeader::addTxToMap(uint16_t txIdx, Tx & tx)
 
368
{
 
369
   StoredTx storedTx;
 
370
   storedTx.createFromTx(tx);
 
371
   addStoredTxToMap(txIdx, storedTx);
 
372
}
 
373
 
 
374
/////////////////////////////////////////////////////////////////////////////
 
375
void StoredHeader::addStoredTxToMap(uint16_t txIdx, StoredTx & stx)
 
376
{
 
377
   if(txIdx >= numTx_)
 
378
   {
 
379
      LOGERR << "TxIdx is greater than numTx of stored header";
 
380
      return;
 
381
   }
 
382
   stxMap_[txIdx] = stx; 
 
383
}
 
384
 
 
385
/////////////////////////////////////////////////////////////////////////////
 
386
void StoredTx::addTxOutToMap(uint16_t idx, TxOut & txout)
 
387
{
 
388
   if(idx >= numTxOut_)
 
389
   {
 
390
      LOGERR << "TxOutIdx is greater than numTxOut of stored tx";
 
391
      return;
 
392
   }
 
393
   StoredTxOut stxo;
 
394
   stxo.unserialize(txout.serialize());
 
395
   stxoMap_[idx] = stxo;
 
396
}
 
397
 
 
398
/////////////////////////////////////////////////////////////////////////////
 
399
void StoredTx::addStoredTxOutToMap(uint16_t idx, StoredTxOut & stxo)
 
400
{
 
401
   if(idx >= numTxOut_)
 
402
   {
 
403
      LOGERR << "TxOutIdx is greater than numTxOut of stored tx";
 
404
      return;
 
405
   }
 
406
   stxoMap_[idx] = stxo;
 
407
}
 
408
 
 
409
/////////////////////////////////////////////////////////////////////////////
 
410
BlockHeader StoredHeader::getBlockHeaderCopy(void) const
 
411
{
 
412
   if(!isInitialized())
 
413
      return BlockHeader(); 
 
414
 
 
415
   BlockHeader bh(dataCopy_);
 
416
 
 
417
   bh.setNumTx(numTx_);
 
418
   bh.setBlockSize(numBytes_);
 
419
   bh.setDuplicateID(duplicateID_);
 
420
 
 
421
   return bh;
 
422
}
 
423
 
 
424
/////////////////////////////////////////////////////////////////////////////
 
425
BinaryData StoredHeader::getSerializedBlockHeader(void) const
 
426
{
 
427
   if(!isInitialized())
 
428
      return BinaryData(0);
 
429
 
 
430
   return dataCopy_;
 
431
}
 
432
 
 
433
////////////////////////////////////////////////////////////////////////////////
 
434
void StoredHeader::unserializeDBValue(DB_SELECT db,
 
435
                                      BinaryData const & bd,
 
436
                                      bool ignoreMerkle)
 
437
{
 
438
   BinaryRefReader brr(bd);
 
439
   unserializeDBValue(db, brr, ignoreMerkle);
 
440
}
 
441
 
 
442
////////////////////////////////////////////////////////////////////////////////
 
443
void StoredHeader::unserializeDBValue(DB_SELECT db,
 
444
                                      BinaryDataRef bdr,
 
445
                                      bool ignoreMerkle)
 
446
{
 
447
   BinaryRefReader brr(bdr);
 
448
   unserializeDBValue(db, brr, ignoreMerkle);
 
449
}
 
450
////////////////////////////////////////////////////////////////////////////////
 
451
BinaryData StoredHeader::serializeDBValue(DB_SELECT db) const
 
452
{
 
453
   BinaryWriter bw;
 
454
   serializeDBValue(db, bw);
 
455
   return bw.getData();
 
456
}
 
457
 
 
458
/////////////////////////////////////////////////////////////////////////////
 
459
void StoredHeader::unserializeDBValue( DB_SELECT         db,
 
460
                                       BinaryRefReader & brr,
 
461
                                       bool              ignoreMerkle)
 
462
{
 
463
 
 
464
   if(db==HEADERS)
 
465
   {
 
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_);
 
471
   }
 
472
   else if(db==BLKDATA)
 
473
   {
 
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();
 
482
   
 
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();
 
488
 
 
489
      if(unserArmVer_ != ARMORY_DB_VERSION)
 
490
         LOGWARN << "Version mismatch in unserialize DB header";
 
491
 
 
492
      if( !ignoreMerkle )
 
493
      {
 
494
         uint32_t currPos = brr.getPosition();
 
495
         uint32_t totalSz = brr.getSize();
 
496
         if(unserMkType_ == MERKLE_SER_NONE)
 
497
            merkle_.resize(0);
 
498
         else
 
499
         {
 
500
            merkleIsPartial_ = (unserMkType_ == MERKLE_SER_PARTIAL);
 
501
            brr.get_BinaryData(merkle_, totalSz - currPos);
 
502
         }
 
503
      }
 
504
   }
 
505
 
 
506
}
 
507
 
 
508
/////////////////////////////////////////////////////////////////////////////
 
509
void StoredHeader::serializeDBValue( DB_SELECT       db,
 
510
                                     BinaryWriter &  bw) const
 
511
{
 
512
   if(!isInitialized())
 
513
   {
 
514
      LOGERR << "Attempted to serialize uninitialized block header";
 
515
      return;
 
516
   }
 
517
 
 
518
   if(db==HEADERS)
 
519
   {
 
520
      BinaryData hgtx = DBUtils.heightAndDupToHgtx(blockHeight_, duplicateID_);
 
521
      bw.put_BinaryData(dataCopy_);
 
522
      bw.put_BinaryData(hgtx);
 
523
   }
 
524
   else if(db==BLKDATA)
 
525
   {
 
526
      uint32_t version = READ_UINT32_LE(dataCopy_.getPtr());
 
527
 
 
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())
 
538
      {
 
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;
 
545
         default: 
 
546
            LOGERR << "Invalid DB mode in serializeStoredHeaderValue";
 
547
      }
 
548
      
 
549
      // Override the above mtype if the merkle data is zero-length
 
550
      if(merkle_.getSize()==0)
 
551
         mtype = MERKLE_SER_NONE;
 
552
   
 
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_);
 
561
 
 
562
      bw.put_BitPacker(bitpack);
 
563
      bw.put_BinaryData(dataCopy_);
 
564
      bw.put_uint32_t(numTx_);
 
565
      bw.put_uint32_t(numBytes_);
 
566
 
 
567
      if( mtype != MERKLE_SER_NONE )
 
568
      {
 
569
         bw.put_BinaryData(merkle_);
 
570
         if(merkle_.getSize()==0)
 
571
            LOGERR << "Expected to serialize merkle tree, but empty string";
 
572
      }
 
573
   }
 
574
}
 
575
 
 
576
 
 
577
/////////////////////////////////////////////////////////////////////////////
 
578
void StoredHeader::unserializeDBKey(DB_SELECT db, BinaryDataRef key)
 
579
{
 
580
   if(db==BLKDATA)
 
581
   {
 
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_);
 
587
      else
 
588
         LOGERR << "Invalid key for StoredHeader";
 
589
   }
 
590
   else
 
591
      LOGERR << "This method not intended for HEADERS DB";
 
592
}
 
593
 
 
594
 
 
595
/////////////////////////////////////////////////////////////////////////////
 
596
void StoredHeader::pprintOneLine(uint32_t indent)
 
597
{
 
598
   for(uint32_t i=0; i<indent; i++)
 
599
      cout << " ";
 
600
   
 
601
   cout << "HEADER: " << thisHash_.getSliceCopy(0,4).toHexStr()
 
602
        << " (" << blockHeight_ << "," << (uint32_t)duplicateID_ << ")"
 
603
        << "     #Tx: " << numTx_
 
604
        << " Applied: " << (blockAppliedToDB_ ? "T" : "F")
 
605
        << endl;
 
606
}
 
607
 
 
608
/////////////////////////////////////////////////////////////////////////////
 
609
void StoredHeader::pprintFullBlock(uint32_t indent)
 
610
{
 
611
   pprintOneLine(indent);
 
612
   if(numTx_ > 10000)
 
613
   {
 
614
      cout << "      <No tx to print>" << endl;
 
615
      return;
 
616
   }
 
617
 
 
618
   for(uint32_t i=0; i<numTx_; i++)
 
619
      stxMap_[i].pprintFullTx(indent+3);
 
620
}
 
621
 
 
622
/////////////////////////////////////////////////////////////////////////////
 
623
BinaryData StoredTx::getDBKey(bool withPrefix) const
 
624
{
 
625
   if(blockHeight_ == UINT32_MAX || 
 
626
      duplicateID_ == UINT8_MAX  || 
 
627
      txIndex_     == UINT16_MAX)
 
628
   {
 
629
      LOGERR << "Requesting DB key for incomplete STX";
 
630
      return BinaryData(0);
 
631
   }
 
632
 
 
633
   if(withPrefix)
 
634
      return DBUtils.getBlkDataKey(blockHeight_, duplicateID_, txIndex_);
 
635
   else
 
636
      return DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_, txIndex_);
 
637
}
 
638
 
 
639
/////////////////////////////////////////////////////////////////////////////
 
640
BinaryData StoredTx::getDBKeyOfChild(uint16_t i, bool withPrefix) const
 
641
{
 
642
   return (getDBKey(withPrefix) + WRITE_UINT16_BE(i));
 
643
}
 
644
 
 
645
/////////////////////////////////////////////////////////////////////////////
 
646
void StoredTx::unserialize(BinaryData const & data, bool fragged)
 
647
{
 
648
   BinaryRefReader brr(data);
 
649
   unserialize(brr, fragged);
 
650
}
 
651
 
 
652
/////////////////////////////////////////////////////////////////////////////
 
653
void StoredTx::unserialize(BinaryDataRef data, bool fragged)
 
654
{
 
655
   BinaryRefReader brr(data);
 
656
   unserialize(brr, fragged);
 
657
}
 
658
 
 
659
 
 
660
/////////////////////////////////////////////////////////////////////////////
 
661
void StoredTx::unserialize(BinaryRefReader & brr, bool fragged)
 
662
{
 
663
   vector<uint32_t> offsetsIn, offsetsOut; 
 
664
   uint32_t nbytes = BtcUtils::StoredTxCalcLength(brr.getCurrPtr(),
 
665
                                                  fragged,
 
666
                                                  &offsetsIn,
 
667
                                                  &offsetsOut);
 
668
   if(brr.getSizeRemaining() < nbytes)
 
669
   {
 
670
      LOGERR << "Not enough bytes in BRR to unserialize StoredTx";
 
671
      return;
 
672
   }
 
673
 
 
674
   brr.get_BinaryData(dataCopy_, nbytes);
 
675
 
 
676
   isFragged_ = fragged;
 
677
   numTxOut_  = offsetsOut.size()-1;
 
678
   version_   = READ_UINT32_LE(dataCopy_.getPtr());
 
679
   lockTime_  = READ_UINT32_LE(dataCopy_.getPtr() + nbytes - 4);
 
680
 
 
681
   if(isFragged_)
 
682
   {
 
683
      fragBytes_ = nbytes;
 
684
      numBytes_ = UINT32_MAX;
 
685
   }
 
686
   else
 
687
   {
 
688
      numBytes_ = nbytes;
 
689
      uint32_t span = offsetsOut[numTxOut_] - offsetsOut[0];
 
690
      fragBytes_ = numBytes_ - span;
 
691
      BtcUtils::getHash256(dataCopy_, thisHash_);
 
692
   }
 
693
}
 
694
 
 
695
 
 
696
////////////////////////////////////////////////////////////////////////////////
 
697
void StoredTx::unserializeDBValue(BinaryData const & bd)
 
698
{
 
699
   BinaryRefReader brr(bd);
 
700
   unserializeDBValue(brr);
 
701
}
 
702
 
 
703
////////////////////////////////////////////////////////////////////////////////
 
704
void StoredTx::unserializeDBValue(BinaryDataRef bdr)
 
705
                                  
 
706
{
 
707
   BinaryRefReader brr(bdr);
 
708
   unserializeDBValue(brr);
 
709
}
 
710
 
 
711
////////////////////////////////////////////////////////////////////////////////
 
712
BinaryData StoredTx::serializeDBValue(void) const
 
713
{
 
714
   BinaryWriter bw;
 
715
   serializeDBValue(bw);
 
716
   return bw.getData();
 
717
}
 
718
 
 
719
 
 
720
 
 
721
/////////////////////////////////////////////////////////////////////////////
 
722
void StoredTx::unserializeDBValue(BinaryRefReader & brr)
 
723
{
 
724
   // flags
 
725
   //    DBVersion      4 bits
 
726
   //    TxVersion      2 bits
 
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);
 
732
 
 
733
   if(unserArmVer_ != ARMORY_DB_VERSION)
 
734
      LOGWARN << "Version mismatch in unserialize DB tx";
 
735
   
 
736
   brr.get_BinaryData(thisHash_, 32);
 
737
 
 
738
   if(unserTxType_ == TX_SER_FULL || unserTxType_ == TX_SER_FRAGGED)
 
739
      unserialize(brr, unserTxType_==TX_SER_FRAGGED);
 
740
   else
 
741
      numTxOut_ = (uint32_t)brr.get_var_int();
 
742
}
 
743
 
 
744
 
 
745
/////////////////////////////////////////////////////////////////////////////
 
746
void StoredTx::serializeDBValue(BinaryWriter & bw) const
 
747
{
 
748
   TX_SERIALIZE_TYPE serType;
 
749
   
 
750
   switch(DBUtils.getArmoryDbType())
 
751
   {
 
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;
 
759
      default: 
 
760
         LOGERR << "Invalid DB mode in serializeStoredTxValue";
 
761
   }
 
762
 
 
763
   if(serType==TX_SER_FULL && !haveAllTxOut())
 
764
   {
 
765
      LOGERR << "Supposed to write out full Tx, but don't have it";
 
766
      return;
 
767
   }
 
768
 
 
769
   if(thisHash_.getSize() == 0)
 
770
   {
 
771
      LOGERR << "Do not know tx hash to be able to DB-serialize StoredTx";
 
772
      return;
 
773
   }
 
774
 
 
775
   uint16_t version = (uint16_t)READ_UINT32_LE(dataCopy_.getPtr());
 
776
 
 
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);
 
781
 
 
782
   
 
783
   bw.put_BitPacker(bitpack);
 
784
   bw.put_BinaryData(thisHash_);
 
785
 
 
786
   if(serType == TX_SER_FULL)
 
787
      bw.put_BinaryData(getSerializedTx());
 
788
   else if(serType == TX_SER_FRAGGED)
 
789
      bw.put_BinaryData(getSerializedTxFragged());
 
790
   else
 
791
      bw.put_var_int(numTxOut_);
 
792
}
 
793
 
 
794
 
 
795
////////////////////////////////////////////////////////////////////////////////
 
796
bool StoredTx::haveAllTxOut(void) const
 
797
{
 
798
   if(!isInitialized())
 
799
      return false; 
 
800
 
 
801
   if(!isFragged_)
 
802
      return true;
 
803
 
 
804
   return stxoMap_.size()==numTxOut_;
 
805
 
 
806
}
 
807
 
 
808
 
 
809
////////////////////////////////////////////////////////////////////////////////
 
810
BinaryData StoredTx::getSerializedTx(void) const
 
811
{
 
812
   if(!isInitialized())
 
813
      return BinaryData(0); 
 
814
 
 
815
   if(!isFragged_)
 
816
      return dataCopy_;
 
817
   else if(!haveAllTxOut())
 
818
      return BinaryData(0); 
 
819
 
 
820
   BinaryWriter bw;
 
821
   bw.reserve(numBytes_);
 
822
    
 
823
   if(numBytes_ != UINT32_MAX)
 
824
      bw.reserve(numBytes_);
 
825
 
 
826
   bw.put_BinaryData(dataCopy_.getPtr(), dataCopy_.getSize()-4);
 
827
 
 
828
   map<uint16_t, StoredTxOut>::const_iterator iter;
 
829
   uint16_t i=0;
 
830
   for(iter = stxoMap_.begin(); iter != stxoMap_.end(); iter++, i++)
 
831
   {
 
832
      if(iter->first != i)
 
833
      {
 
834
         LOGERR << "Indices out of order accessing stxoMap_...?!";
 
835
         return BinaryData(0);
 
836
      }
 
837
      bw.put_BinaryData(iter->second.getSerializedTxOut());
 
838
   }
 
839
 
 
840
   bw.put_BinaryData(dataCopy_.getPtr()+dataCopy_.getSize()-4, 4);
 
841
   return bw.getData();
 
842
}
 
843
 
 
844
////////////////////////////////////////////////////////////////////////////////
 
845
BinaryData StoredTx::getSerializedTxFragged(void) const
 
846
{
 
847
   if(!isInitialized())
 
848
      return BinaryData(0); 
 
849
 
 
850
   if(isFragged_)
 
851
      return dataCopy_;
 
852
 
 
853
   if(numBytes_ == UINT32_MAX)
 
854
   {
 
855
      LOGERR << "Do not know size of tx in order to serialize it";
 
856
      return BinaryData(0);
 
857
   }
 
858
 
 
859
   BinaryWriter bw;
 
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;
 
865
 
 
866
   BinaryData output(dataCopy_.getSize() - span);
 
867
   dataCopy_.getSliceRef(0,  firstOut).copyTo(output.getPtr());
 
868
   dataCopy_.getSliceRef(afterLast, 4).copyTo(output.getPtr()+firstOut);
 
869
   return output;
 
870
}
 
871
 
 
872
/////////////////////////////////////////////////////////////////////////////
 
873
void StoredTx::unserializeDBKey(BinaryDataRef key)
 
874
{
 
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_);
 
880
   else
 
881
      LOGERR << "Invalid key for StoredTx";
 
882
}
 
883
 
 
884
////////////////////////////////////////////////////////////////////////////////
 
885
void StoredTx::pprintOneLine(uint32_t indent)
 
886
{
 
887
   for(uint32_t i=0; i<indent; i++)
 
888
      cout << " ";
 
889
   
 
890
   cout << "TX:  " << thisHash_.getSliceCopy(0,4).toHexStr()
 
891
        << " (" << blockHeight_ 
 
892
        << "," << (uint32_t)duplicateID_ 
 
893
        << "," << txIndex_ << ")"
 
894
        << "   #TXO: " << numTxOut_
 
895
        << endl;
 
896
}
 
897
 
 
898
////////////////////////////////////////////////////////////////////////////////
 
899
void StoredTx::pprintFullTx(uint32_t indent)
 
900
{
 
901
   pprintOneLine(indent);
 
902
   if(numTxOut_ > 10000)
 
903
   {
 
904
      cout << "         <No txout to print>" << endl;
 
905
      return;
 
906
   }
 
907
 
 
908
   for(uint32_t i=0; i<numTxOut_; i++)
 
909
      stxoMap_[i].pprintOneLine(indent+3);
 
910
}
 
911
 
 
912
////////////////////////////////////////////////////////////////////////////////
 
913
void StoredTxOut::unserialize(BinaryData const & data)
 
914
{
 
915
   BinaryRefReader brr(data);
 
916
   unserialize(brr);
 
917
}
 
918
 
 
919
////////////////////////////////////////////////////////////////////////////////
 
920
void StoredTxOut::unserialize(BinaryDataRef data)
 
921
{
 
922
   BinaryRefReader brr(data);
 
923
   unserialize(brr);
 
924
}
 
925
 
 
926
////////////////////////////////////////////////////////////////////////////////
 
927
void StoredTxOut::unserialize(BinaryRefReader & brr)
 
928
{
 
929
   if(brr.getSizeRemaining() < 8)
 
930
   {
 
931
      LOGERR << "Not enough bytes in BRR to unserialize StoredTxOut";
 
932
      return;
 
933
   }
 
934
 
 
935
   uint32_t numBytes = BtcUtils::TxOutCalcLength(brr.getCurrPtr());
 
936
 
 
937
   if(brr.getSizeRemaining() < numBytes)
 
938
   {
 
939
      LOGERR << "Not enough bytes in BRR to unserialize StoredTxOut";
 
940
      return;
 
941
   }
 
942
 
 
943
   brr.get_BinaryData(dataCopy_, numBytes);
 
944
}
 
945
 
 
946
 
 
947
////////////////////////////////////////////////////////////////////////////////
 
948
void StoredTxOut::unserializeDBValue(BinaryData const & bd)
 
949
{
 
950
   BinaryRefReader brr(bd);
 
951
   unserializeDBValue(brr);
 
952
}
 
953
 
 
954
////////////////////////////////////////////////////////////////////////////////
 
955
void StoredTxOut::unserializeDBValue(BinaryDataRef bdr)
 
956
{
 
957
   BinaryRefReader brr(bdr);
 
958
   unserializeDBValue(brr);
 
959
}
 
960
 
 
961
////////////////////////////////////////////////////////////////////////////////
 
962
void StoredTxOut::unserializeDBValue(BinaryRefReader & brr)
 
963
{
 
964
   // Similar to TxValue flags
 
965
   //    DBVersion   4 bits
 
966
   //    TxVersion   2 bits
 
967
   //    Spentness   2 bits
 
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();
 
973
 
 
974
   unserialize(brr);
 
975
   if(spentness_ == TXOUT_SPENT && brr.getSizeRemaining()>=8)
 
976
      spentByTxInKey_ = brr.get_BinaryData(8); 
 
977
 
 
978
}
 
979
 
 
980
 
 
981
////////////////////////////////////////////////////////////////////////////////
 
982
BinaryData StoredTxOut::serializeDBValue(bool forceSaveSpent) const
 
983
{
 
984
   BinaryWriter bw;
 
985
   serializeDBValue(bw, forceSaveSpent);
 
986
   return bw.getData();
 
987
}
 
988
 
 
989
////////////////////////////////////////////////////////////////////////////////
 
990
void StoredTxOut::serializeDBValue(BinaryWriter & bw,
 
991
                                   bool forceSaveSpentness) const
 
992
{
 
993
   TXOUT_SPENTNESS writeSpent = spentness_;
 
994
   
 
995
   if(!forceSaveSpentness)
 
996
   { 
 
997
      switch(DBUtils.getArmoryDbType())
 
998
      {
 
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;
 
1007
         default: 
 
1008
            LOGERR << "Invalid DB mode in serializeStoredTxOutValue";
 
1009
      }
 
1010
   }
 
1011
 
 
1012
   uint16_t isCbase = (isCoinbase_ ? 1 : 0);
 
1013
 
 
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_);
 
1019
 
 
1020
   bw.put_BitPacker(bitpack);
 
1021
   bw.put_BinaryData(dataCopy_);  // 8-byte value, var_int sz, pkscript
 
1022
   
 
1023
   if(writeSpent == TXOUT_SPENT)
 
1024
   {
 
1025
      if(spentByTxInKey_.getSize()==0)
 
1026
         LOGERR << "Need to write out spentByTxIn but no spentness data";
 
1027
      bw.put_BinaryData(spentByTxInKey_);
 
1028
   }
 
1029
 
 
1030
}
 
1031
 
 
1032
/////////////////////////////////////////////////////////////////////////////
 
1033
BinaryData StoredTxOut::getDBKey(bool withPrefix) const
 
1034
{
 
1035
   if(blockHeight_ == UINT32_MAX || 
 
1036
      duplicateID_ == UINT8_MAX  || 
 
1037
      txIndex_     == UINT16_MAX ||
 
1038
      txOutIndex_  == UINT16_MAX)
 
1039
   {
 
1040
      LOGERR << "Requesting DB key for incomplete STXO";
 
1041
      return BinaryData(0);
 
1042
   }
 
1043
 
 
1044
   if(withPrefix)
 
1045
      return DBUtils.getBlkDataKey(
 
1046
                             blockHeight_, duplicateID_, txIndex_, txOutIndex_);
 
1047
   else
 
1048
      return DBUtils.getBlkDataKeyNoPrefix(
 
1049
                             blockHeight_, duplicateID_, txIndex_, txOutIndex_);
 
1050
}
 
1051
 
 
1052
////////////////////////////////////////////////////////////////////////////////
 
1053
BinaryData StoredTxOut::getDBKeyOfParentTx(bool withPrefix) const
 
1054
{
 
1055
   BinaryData stxoKey = getDBKey(withPrefix);
 
1056
   if(withPrefix)
 
1057
      return stxoKey.getSliceCopy(0, 7);
 
1058
   else
 
1059
      return stxoKey.getSliceCopy(0, 6);
 
1060
}
 
1061
 
 
1062
////////////////////////////////////////////////////////////////////////////////
 
1063
bool StoredTxOut::matchesDBKey(BinaryDataRef dbkey) const
 
1064
{
 
1065
   if(dbkey.getSize() == 8)
 
1066
      return (getDBKey(false) == dbkey);
 
1067
   else if(dbkey.getSize() == 9)
 
1068
      return (getDBKey(true) == dbkey);
 
1069
   else
 
1070
   {
 
1071
      LOGERR << "Non STXO-DBKey passed in to check match against STXO";
 
1072
      return false;
 
1073
   }
 
1074
}
 
1075
 
 
1076
 
 
1077
////////////////////////////////////////////////////////////////////////////////
 
1078
Tx StoredTx::getTxCopy(void) const
 
1079
{
 
1080
   if(!haveAllTxOut())
 
1081
   {
 
1082
      LOGERR << "Cannot get tx copy, because don't have full StoredTx!";
 
1083
      return Tx();
 
1084
   }
 
1085
   
 
1086
   Tx returnTx(getSerializedTx());
 
1087
   if(blockHeight_ != UINT32_MAX)
 
1088
      returnTx.setTxRef(TxRef(getDBKey(false)));
 
1089
   return returnTx;
 
1090
}
 
1091
 
 
1092
////////////////////////////////////////////////////////////////////////////////
 
1093
void StoredTx::setKeyData(uint32_t height, uint8_t dup, uint16_t txIdx)
 
1094
{
 
1095
   blockHeight_ = height;
 
1096
   duplicateID_ = dup;
 
1097
   txIndex_     = txIdx;
 
1098
 
 
1099
   map<uint16_t, StoredTxOut>::iterator iter;
 
1100
   for(iter  = stxoMap_.begin();  iter != stxoMap_.end(); iter++)
 
1101
   {
 
1102
      iter->second.blockHeight_ = height;
 
1103
      iter->second.duplicateID_ = dup;
 
1104
      iter->second.txIndex_     = txIdx;
 
1105
      iter->second.txOutIndex_  = iter->first;
 
1106
   }
 
1107
}
 
1108
 
 
1109
////////////////////////////////////////////////////////////////////////////////
 
1110
StoredTx & StoredTx::createFromTx(BinaryDataRef rawTx, bool doFrag, bool withTxOuts)
 
1111
{
 
1112
   Tx tx(rawTx);
 
1113
   return createFromTx(tx, doFrag, withTxOuts);
 
1114
}
 
1115
 
 
1116
////////////////////////////////////////////////////////////////////////////////
 
1117
StoredTx & StoredTx::createFromTx(Tx & tx, bool doFrag, bool withTxOuts)
 
1118
{
 
1119
   if(!tx.isInitialized())
 
1120
   {
 
1121
      LOGERR << "Creating storedtx from uninitialized tx. Aborting.";
 
1122
      dataCopy_.resize(0);
 
1123
      return *this;
 
1124
   }
 
1125
 
 
1126
   thisHash_  = tx.getThisHash();
 
1127
   numTxOut_  = tx.getNumTxOut();
 
1128
   version_   = tx.getVersion();
 
1129
   lockTime_  = tx.getLockTime(); 
 
1130
   numBytes_  = tx.getSize(); 
 
1131
   isFragged_ = doFrag;
 
1132
 
 
1133
   uint32_t span = tx.getTxOutOffset(numTxOut_) - tx.getTxOutOffset(0);
 
1134
   fragBytes_ = numBytes_ - span;
 
1135
 
 
1136
   if(!doFrag)
 
1137
      dataCopy_ = tx.serialize(); 
 
1138
   else
 
1139
   {
 
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);
 
1146
      brr.advance(span);
 
1147
      brr.get_BinaryData(dataCopy_.getPtr()+firstOut, 4);
 
1148
   }
 
1149
 
 
1150
   if(withTxOuts)
 
1151
   {
 
1152
      for(uint32_t txo = 0; txo < tx.getNumTxOut(); txo++)
 
1153
      {
 
1154
         stxoMap_[txo] = StoredTxOut();
 
1155
         StoredTxOut & stxo = stxoMap_[txo];
 
1156
 
 
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();
 
1162
      }
 
1163
   }
 
1164
 
 
1165
   return *this;
 
1166
}
 
1167
 
 
1168
 
 
1169
////////////////////////////////////////////////////////////////////////////////
 
1170
StoredTxOut & StoredTxOut::createFromTxOut(TxOut & txout)
 
1171
{
 
1172
   unserialize(txout.serialize());
 
1173
   return *this;
 
1174
}
 
1175
 
 
1176
////////////////////////////////////////////////////////////////////////////////
 
1177
BinaryData StoredTxOut::getSerializedTxOut(void) const
 
1178
{
 
1179
   if(!isInitialized())
 
1180
   {
 
1181
      LOGERR << "Attempted to get serialized TxOut, but not initialized";
 
1182
      return BinaryData(0);
 
1183
   }
 
1184
   return dataCopy_;
 
1185
}
 
1186
 
 
1187
////////////////////////////////////////////////////////////////////////////////
 
1188
TxOut StoredTxOut::getTxOutCopy(void) const
 
1189
{
 
1190
   if(!isInitialized())
 
1191
   {
 
1192
      LOGERR << "Attempted to get TxOut copy but not initialized";
 
1193
      return TxOut();
 
1194
   }
 
1195
   TxOut o;
 
1196
   o.unserialize_checked(dataCopy_.getPtr(), dataCopy_.getSize());
 
1197
   return o;
 
1198
}
 
1199
 
 
1200
////////////////////////////////////////////////////////////////////////////////
 
1201
BinaryData StoredTxOut::getScrAddress(void) const
 
1202
{
 
1203
   BinaryRefReader brr(dataCopy_);
 
1204
   brr.advance(8);
 
1205
   uint32_t scrsz = (uint32_t)brr.get_var_int();
 
1206
   return BtcUtils::getTxOutScrAddr(brr.get_BinaryDataRef(scrsz));
 
1207
}
 
1208
 
 
1209
////////////////////////////////////////////////////////////////////////////////
 
1210
BinaryDataRef StoredTxOut::getScriptRef(void) const
 
1211
{
 
1212
   BinaryRefReader brr(dataCopy_);
 
1213
   brr.advance(8);
 
1214
   uint32_t scrsz = (uint32_t)brr.get_var_int();
 
1215
   return brr.get_BinaryDataRef(scrsz);
 
1216
}
 
1217
 
 
1218
////////////////////////////////////////////////////////////////////////////////
 
1219
uint64_t StoredTxOut::getValue(void) const
 
1220
{
 
1221
   if(!isInitialized())
 
1222
      return UINT64_MAX;
 
1223
 
 
1224
   return *(uint64_t*)dataCopy_.getPtr();
 
1225
}
 
1226
 
 
1227
/////////////////////////////////////////////////////////////////////////////
 
1228
void StoredTxOut::unserializeDBKey(BinaryDataRef key)
 
1229
{
 
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_);
 
1235
   else
 
1236
      LOGERR << "Invalid key for StoredTxOut";
 
1237
}
 
1238
 
 
1239
////////////////////////////////////////////////////////////////////////////////
 
1240
void StoredTxOut::pprintOneLine(uint32_t indent)
 
1241
{
 
1242
   for(uint32_t i=0; i<indent; i++)
 
1243
      cout << " ";
 
1244
 
 
1245
   string pprintHash("");
 
1246
   if(parentHash_.getSize() > 0)
 
1247
      pprintHash = parentHash_.getSliceCopy(0,4).toHexStr();
 
1248
  
 
1249
   cout << "TXOUT:   "
 
1250
        << "  (" << blockHeight_ 
 
1251
        << "," << (uint32_t)duplicateID_ 
 
1252
        << "," << txIndex_
 
1253
        << "," << txOutIndex_ << ")"
 
1254
        << " Value=" << (double)(getValue())/(100000000.0)
 
1255
        << " isCB: " << (isCoinbase_ ? "(X)" : "   ");
 
1256
 
 
1257
   if(spentness_ == TXOUT_SPENTUNK)
 
1258
        cout << " Spnt: " << "<-----UNKNOWN---->" << endl;
 
1259
   else if(spentness_ == TXOUT_UNSPENT)
 
1260
        cout << " Spnt: " << "<                >" << endl;
 
1261
   else 
 
1262
        cout << " Spnt: " << "<" << spentByTxInKey_.toHexStr() << ">" << endl;
 
1263
}
 
1264
 
 
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)
 
1277
{
 
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();
 
1286
 
 
1287
   alreadyScannedUpToBlk_ = brr.get_uint32_t();
 
1288
   totalTxioCount_ = brr.get_var_int();
 
1289
 
 
1290
   // We shouldn't end up with empty SSH's, but should catch it just in case
 
1291
   if(totalTxioCount_==0)
 
1292
      return;
 
1293
   
 
1294
   subHistMap_.clear();
 
1295
   if(useMultipleEntries_)
 
1296
      totalUnspent_ = brr.get_uint64_t();
 
1297
   else
 
1298
   {
 
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();
 
1307
 
 
1308
      // We always include the 8-byte value
 
1309
      uint64_t txoValue  = brr.get_uint64_t();
 
1310
 
 
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);
 
1315
 
 
1316
      totalUnspent_ = 0;
 
1317
      if(isSpent)
 
1318
         txio.setTxIn(brr.get_BinaryDataRef(8));
 
1319
      else
 
1320
      {
 
1321
         if(!isMulti) 
 
1322
            totalUnspent_ = txoValue;
 
1323
      }
 
1324
 
 
1325
      txio.setTxOutFromSelf(isFromSelf);
 
1326
      txio.setFromCoinbase(isCoinbase);
 
1327
      txio.setMultisig(isMulti);
 
1328
  
 
1329
      // The second "true" is to tell the insert function to skip incrementing
 
1330
      // the totalUnspent_ and totalTxioCount_, since that data is already
 
1331
      // correct.  
 
1332
      insertTxio(txio, true, true); 
 
1333
   
 
1334
   }
 
1335
}
 
1336
 
 
1337
////////////////////////////////////////////////////////////////////////////////
 
1338
void StoredScriptHistory::serializeDBValue(BinaryWriter & bw ) const
 
1339
{
 
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);
 
1347
 
 
1348
   // 
 
1349
   bw.put_uint32_t(alreadyScannedUpToBlk_); 
 
1350
   bw.put_var_int(totalTxioCount_); 
 
1351
 
 
1352
   // We shouldn't end up with empty SSH's, but should catch it just in case
 
1353
   if(totalTxioCount_==0)
 
1354
      return;
 
1355
 
 
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_);
 
1361
   else
 
1362
   {
 
1363
      if(subHistMap_.size() != 1)
 
1364
      {
 
1365
         LOGERR << "!multi entry but " << subHistMap_.size() << " TxIOs?";
 
1366
         LOGERR << uniqueKey_.toHexStr().c_str();
 
1367
         return;
 
1368
      }
 
1369
 
 
1370
      map<BinaryData, StoredSubHistory>::const_iterator iter;
 
1371
      iter = subHistMap_.begin();
 
1372
      if(iter->second.txioSet_.size() != 1)
 
1373
      {
 
1374
         LOGERR << "One subSSH but " << iter->second.txioSet_.size() << " TxIOs?";
 
1375
         return;
 
1376
      }
 
1377
 
 
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();
 
1381
 
 
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);
 
1388
 
 
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);
 
1392
 
 
1393
      // If not supposed to write the TxIn, we would've bailed earlier
 
1394
      if(txio.hasTxInInMain())
 
1395
         bw.put_BinaryData(txio.getDBKeyOfInput());
 
1396
   }
 
1397
}
 
1398
 
 
1399
 
 
1400
////////////////////////////////////////////////////////////////////////////////
 
1401
void StoredScriptHistory::unserializeDBValue(BinaryData const & bd)
 
1402
{
 
1403
   BinaryRefReader brr(bd);
 
1404
   unserializeDBValue(brr);
 
1405
}
 
1406
 
 
1407
////////////////////////////////////////////////////////////////////////////////
 
1408
void StoredScriptHistory::unserializeDBValue(BinaryDataRef bdr)
 
1409
                                  
 
1410
{
 
1411
   BinaryRefReader brr(bdr);
 
1412
   unserializeDBValue(brr);
 
1413
}
 
1414
 
 
1415
////////////////////////////////////////////////////////////////////////////////
 
1416
BinaryData StoredScriptHistory::serializeDBValue(void) const
 
1417
{
 
1418
   BinaryWriter bw;
 
1419
   serializeDBValue(bw);
 
1420
   return bw.getData();
 
1421
}
 
1422
 
 
1423
////////////////////////////////////////////////////////////////////////////////
 
1424
BinaryData StoredScriptHistory::getDBKey(bool withPrefix) const
 
1425
{
 
1426
   BinaryWriter bw(1+uniqueKey_.getSize());
 
1427
   if(withPrefix)
 
1428
      bw.put_uint8_t((uint8_t)DB_PREFIX_SCRIPT); 
 
1429
   
 
1430
   bw.put_BinaryData(uniqueKey_);
 
1431
   return bw.getData();
 
1432
}
 
1433
 
 
1434
 
 
1435
////////////////////////////////////////////////////////////////////////////////
 
1436
SCRIPT_PREFIX StoredScriptHistory::getScriptType(void) const
 
1437
{
 
1438
   if(uniqueKey_.getSize() == 0)
 
1439
      return SCRIPT_PREFIX_NONSTD;
 
1440
   else
 
1441
      return (SCRIPT_PREFIX)uniqueKey_[0];
 
1442
}
 
1443
 
 
1444
 
 
1445
/////////////////////////////////////////////////////////////////////////////
 
1446
void StoredScriptHistory::unserializeDBKey(BinaryDataRef key, bool withPrefix)
 
1447
{
 
1448
   // Assume prefix
 
1449
   if(withPrefix)
 
1450
      uniqueKey_ = key.getSliceCopy(1, key.getSize()-1);
 
1451
   else
 
1452
      uniqueKey_ = key;
 
1453
}
 
1454
 
 
1455
////////////////////////////////////////////////////////////////////////////////
 
1456
void StoredScriptHistory::pprintOneLine(uint32_t indent)
 
1457
{
 
1458
   for(uint32_t i=0; i<indent; i++)
 
1459
      cout << " ";
 
1460
 
 
1461
   string ktype;
 
1462
   if(uniqueKey_[0] == SCRIPT_PREFIX_HASH160)
 
1463
      ktype = "H160";
 
1464
   else if(uniqueKey_[0] == SCRIPT_PREFIX_P2SH)
 
1465
      ktype = "P2SH";
 
1466
   else if(uniqueKey_[0] == SCRIPT_PREFIX_MULTISIG)
 
1467
      ktype = "MSIG";
 
1468
   else if(uniqueKey_[0] == SCRIPT_PREFIX_NONSTD)
 
1469
      ktype = "NSTD";
 
1470
   
 
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)
 
1477
        << endl;
 
1478
}
 
1479
 
 
1480
 
 
1481
////////////////////////////////////////////////////////////////////////////////
 
1482
void StoredScriptHistory::pprintFullSSH(uint32_t indent)
 
1483
{
 
1484
   pprintOneLine(indent);
 
1485
 
 
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);
 
1490
}
 
1491
 
 
1492
 
 
1493
 
 
1494
////////////////////////////////////////////////////////////////////////////////
 
1495
TxIOPair* StoredScriptHistory::findTxio(BinaryData const & dbKey8B, 
 
1496
                                        bool includeMultisig)
 
1497
{
 
1498
   if(!isInitialized() || subHistMap_.size() == 0)
 
1499
      return NULL;
 
1500
 
 
1501
   // Optimize case of 1 txio -- don't bother with extra copies or map.find ops
 
1502
   if(totalTxioCount_ == 1)
 
1503
   {
 
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)
 
1507
      {
 
1508
         LOGERR << "totalTxioCount_ and subHistMap_.size do not agree!";
 
1509
         return NULL;
 
1510
      }
 
1511
 
 
1512
      StoredSubHistory & subSSH = subHistMap_.begin()->second;
 
1513
      if(subSSH.txioSet_.size() != 1)
 
1514
      {
 
1515
         LOGERR << "totalTxioCount_ and subSSH.txioSet_.size() do not agree!";
 
1516
         return NULL;
 
1517
      }
 
1518
 
 
1519
      TxIOPair* outptr = &(subSSH.txioSet_.begin()->second);
 
1520
      if(!includeMultisig && outptr->isMultisig())
 
1521
         return NULL;
 
1522
 
 
1523
      return (outptr->getDBKeyOfOutput() == dbKey8B ? outptr : NULL);
 
1524
   }
 
1525
   else
 
1526
   {
 
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_))
 
1532
         return NULL;
 
1533
   
 
1534
      // This returns NULL if does not exist.
 
1535
      TxIOPair* outptr = iterSubSSH->second.findTxio(dbKey8B);
 
1536
      if(outptr==NULL)
 
1537
         return NULL;
 
1538
 
 
1539
      if(!includeMultisig && outptr->isMultisig())
 
1540
         return NULL;
 
1541
 
 
1542
      return outptr;
 
1543
   }
 
1544
}
 
1545
 
 
1546
////////////////////////////////////////////////////////////////////////////////
 
1547
TxIOPair& StoredScriptHistory::insertTxio(TxIOPair const & txio, 
 
1548
                                          bool withOverwrite,
 
1549
                                          bool skipTally)
 
1550
{
 
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_))
 
1556
   {
 
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;
 
1561
      if(!skipTally)
 
1562
      {
 
1563
         totalTxioCount_ += 1;
 
1564
         if(!txio.hasTxInInMain() && !txio.isMultisig())
 
1565
            totalUnspent_ += txio.getValue();
 
1566
         useMultipleEntries_ = (totalTxioCount_>1);
 
1567
      }
 
1568
      return subHistMap_[first4].insertTxio(txio, withOverwrite);
 
1569
   }
 
1570
   else
 
1571
   {
 
1572
      // We have sub-history already, though not sure about this specific Txio
 
1573
      if(iterSubHist->second.findTxio(dbKey8) == NULL && !skipTally)
 
1574
      {
 
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);
 
1580
      }
 
1581
      return iterSubHist->second.insertTxio(txio, withOverwrite); 
 
1582
   }
 
1583
}
 
1584
 
 
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)
 
1590
{
 
1591
   return eraseTxio(txio.getDBKeyOfOutput());
 
1592
}
 
1593
 
 
1594
////////////////////////////////////////////////////////////////////////////////
 
1595
bool StoredScriptHistory::eraseTxio(BinaryData const & dbKey8B)
 
1596
{
 
1597
   if(!isInitialized())
 
1598
      return false;
 
1599
 
 
1600
   if(dbKey8B.getSize() != 8)
 
1601
   {
 
1602
      LOGERR << "Invalid dbKey: " << dbKey8B.toHexStr().c_str();
 
1603
      return false;
 
1604
   }
 
1605
 
 
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_))
 
1610
      return false;
 
1611
 
 
1612
   StoredSubHistory & subssh = iterSubHist->second;
 
1613
   uint64_t valueRemoved = subssh.eraseTxio(dbKey8B);
 
1614
 
 
1615
   bool wasRemoved = (valueRemoved!=UINT64_MAX);
 
1616
   if(wasRemoved)
 
1617
   {
 
1618
      totalTxioCount_ -= 1;
 
1619
      totalUnspent_   -= valueRemoved;
 
1620
   }
 
1621
 
 
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
 
1626
   // in the DB. 
 
1627
   //if(iterSubHist->second.txioSet_.size() == 0)
 
1628
      //subHistMap_.erase(iterSubHist);
 
1629
 
 
1630
   return wasRemoved;
 
1631
}
 
1632
 
 
1633
 
 
1634
////////////////////////////////////////////////////////////////////////////////
 
1635
bool StoredScriptHistory::mergeSubHistory(StoredSubHistory & subssh)
 
1636
{
 
1637
   if(uniqueKey_ != subssh.uniqueKey_)
 
1638
   {
 
1639
      LOGERR << "Attempting to add sub-SSH to incorrect SSH";
 
1640
      return false;
 
1641
   }
 
1642
 
 
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);
 
1648
   
 
1649
   bool alreadyExisted = !insResult.second;
 
1650
   if(alreadyExisted)
 
1651
   {
 
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();
 
1659
          iter++)
 
1660
      {
 
1661
         subsshAlreadyInRAM.txioSet_[iter->first] = iter->second;
 
1662
      }
 
1663
   }
 
1664
   return true;
 
1665
}
 
1666
 
 
1667
 
 
1668
 
 
1669
////////////////////////////////////////////////////////////////////////////////
 
1670
// This adds the TxOut if it doesn't exist yet
 
1671
uint64_t StoredScriptHistory::markTxOutSpent(BinaryData txOutKey8B, 
 
1672
                                             BinaryData txInKey8B)
 
1673
{
 
1674
   if(!isInitialized())
 
1675
      return UINT64_MAX;
 
1676
 
 
1677
   if(txOutKey8B.getSize() != 8 || txInKey8B.getSize() != 8)
 
1678
   {
 
1679
      LOGERR << "Invalid input to mark TxOut spent";
 
1680
      LOGERR << "TxOutKey: '" << txOutKey8B.toHexStr().c_str() << "'";
 
1681
      LOGERR << "TxInKey:  '" <<  txInKey8B.toHexStr().c_str() << "'";
 
1682
      return UINT64_MAX;
 
1683
   }
 
1684
 
 
1685
   BinaryData first4 = txOutKey8B.getSliceCopy(0,4);
 
1686
   map<BinaryData, StoredSubHistory>::iterator iter;
 
1687
   iter = subHistMap_.find(first4);
 
1688
 
 
1689
   if(ITER_NOT_IN_MAP(iter, subHistMap_))
 
1690
   {
 
1691
      LOGWARN << "Trying to mark TxIO spent, but does not exist!";
 
1692
      return UINT64_MAX;
 
1693
   }
 
1694
 
 
1695
   uint64_t val = iter->second.markTxOutSpent(txOutKey8B, txInKey8B);
 
1696
   if(val != UINT64_MAX)
 
1697
      totalUnspent_ -= val;
 
1698
 
 
1699
   return val;
 
1700
}
 
1701
 
 
1702
////////////////////////////////////////////////////////////////////////////////
 
1703
uint64_t StoredScriptHistory::markTxOutUnspent(BinaryData txOutKey8B, 
 
1704
                                               uint64_t   value,
 
1705
                                               bool       isCoinbase,
 
1706
                                               bool       isMultisig)
 
1707
{
 
1708
   if(!isInitialized())
 
1709
      return UINT64_MAX;
 
1710
 
 
1711
   if(txOutKey8B.getSize() != 8)
 
1712
   {
 
1713
      LOGERR << "Invalid input to mark TxOut unspent";
 
1714
      LOGERR << "TxOutKey: '" << txOutKey8B.toHexStr().c_str() << "'";
 
1715
      return UINT64_MAX;
 
1716
   }
 
1717
 
 
1718
   BinaryData first4 = txOutKey8B.getSliceCopy(0,4);
 
1719
   map<BinaryData, StoredSubHistory>::iterator iter;
 
1720
   iter = subHistMap_.find(first4);
 
1721
 
 
1722
   if(ITER_NOT_IN_MAP(iter, subHistMap_))
 
1723
   {
 
1724
      // The SubHistory doesn't actually exist yet, so we have to add it
 
1725
      if(value == UINT64_MAX)
 
1726
      {
 
1727
         LOGERR << "Tried to create TxOut in SSH but no value supplied!";
 
1728
         return UINT64_MAX;
 
1729
      }
 
1730
      pair<BinaryData, StoredSubHistory> toInsert(first4, StoredSubHistory());
 
1731
      iter = subHistMap_.insert(toInsert).first;
 
1732
      iter->second.uniqueKey_ = uniqueKey_;
 
1733
      iter->second.hgtX_      = first4;
 
1734
   }
 
1735
 
 
1736
   // More sanity checking
 
1737
   if(ITER_NOT_IN_MAP(iter, subHistMap_))
 
1738
   {
 
1739
      LOGERR << "Somehow still don't have the subSSH after trying to insert it";
 
1740
      return UINT64_MAX;
 
1741
   }
 
1742
 
 
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();
 
1747
 
 
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);
 
1754
 
 
1755
   return val;
 
1756
}
 
1757
 
 
1758
 
 
1759
 
 
1760
////////////////////////////////////////////////////////////////////////////////
 
1761
bool StoredScriptHistory::haveFullHistoryLoaded(void) const
 
1762
{
 
1763
   if(!isInitialized())
 
1764
      return false;
 
1765
 
 
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();
 
1770
 
 
1771
   if(numTxio > totalTxioCount_)
 
1772
      LOGERR << "Somehow stored total is less than counted total...?";
 
1773
 
 
1774
   return (numTxio==totalTxioCount_);
 
1775
}
 
1776
 
 
1777
////////////////////////////////////////////////////////////////////////////////
 
1778
uint64_t StoredScriptHistory::getScriptReceived(bool withMultisig)
 
1779
{
 
1780
   if(!haveFullHistoryLoaded())
 
1781
      return UINT64_MAX;
 
1782
 
 
1783
   uint64_t bal = 0;
 
1784
   map<BinaryData, StoredSubHistory>::iterator iter;
 
1785
   for(iter = subHistMap_.begin(); iter != subHistMap_.end(); iter++)
 
1786
      bal += iter->second.getSubHistoryReceived(withMultisig);
 
1787
 
 
1788
   return bal;
 
1789
}
 
1790
 
 
1791
////////////////////////////////////////////////////////////////////////////////
 
1792
uint64_t StoredScriptHistory::getScriptBalance(bool withMultisig)
 
1793
{
 
1794
   // If regular balance, 
 
1795
   if(!withMultisig)
 
1796
      return totalUnspent_;
 
1797
 
 
1798
   // If with multisig we have to load and count everything
 
1799
   if(!haveFullHistoryLoaded())
 
1800
      return UINT64_MAX;
 
1801
 
 
1802
   uint64_t bal = 0;
 
1803
   map<BinaryData, StoredSubHistory>::iterator iter;
 
1804
   for(iter = subHistMap_.begin(); iter != subHistMap_.end(); iter++)
 
1805
      bal += iter->second.getSubHistoryBalance(withMultisig);
 
1806
 
 
1807
   return bal;
 
1808
}
 
1809
 
 
1810
////////////////////////////////////////////////////////////////////////////////
 
1811
bool StoredScriptHistory::getFullTxioMap( map<BinaryData, TxIOPair> & mapToFill,
 
1812
                                          bool withMultisig)
 
1813
{
 
1814
   if(!haveFullHistoryLoaded())
 
1815
      return false;
 
1816
 
 
1817
   map<BinaryData, StoredSubHistory>::iterator iterSubSSH;
 
1818
   for(iterSubSSH  = subHistMap_.begin(); 
 
1819
       iterSubSSH != subHistMap_.end(); 
 
1820
       iterSubSSH++)
 
1821
   {
 
1822
      StoredSubHistory & subssh = iterSubSSH->second;
 
1823
 
 
1824
      if(withMultisig)
 
1825
      {
 
1826
         // If with multisig, we can just copy everything
 
1827
         mapToFill.insert(subssh.txioSet_.begin(), subssh.txioSet_.end());
 
1828
      }
 
1829
      else
 
1830
      {
 
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();
 
1835
             iterTxio++)
 
1836
         {
 
1837
            if(!iterTxio->second.isMultisig())
 
1838
               mapToFill[iterTxio->first] = iterTxio->second;
 
1839
         }
 
1840
         
 
1841
      }
 
1842
   }
 
1843
   return true;
 
1844
}
 
1845
 
 
1846
 
 
1847
 
 
1848
////////////////////////////////////////////////////////////////////////////////
 
1849
// SubSSH object code
 
1850
//
 
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)
 
1857
{
 
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)
 
1862
   {
 
1863
      LOGERR << "Cannot unserialize DB value until key is set (hgt&dup)";
 
1864
      uniqueKey_.resize(0);
 
1865
      return;
 
1866
   }
 
1867
 
 
1868
   BinaryData fullTxOutKey(8);
 
1869
   hgtX_.copyTo(fullTxOutKey.getPtr());
 
1870
 
 
1871
   uint32_t numTxo = (uint32_t)(brr.get_var_int());
 
1872
   for(uint32_t i=0; i<numTxo; i++)
 
1873
   {
 
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();
 
1879
 
 
1880
      // We always include the 8-byte value
 
1881
      uint64_t txoValue  = brr.get_uint64_t();
 
1882
 
 
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);
 
1887
 
 
1888
      if(isSpent)
 
1889
         txio.setTxIn(brr.get_BinaryDataRef(8));
 
1890
 
 
1891
      txio.setTxOutFromSelf(isFromSelf);
 
1892
      txio.setFromCoinbase(isCoinbase);
 
1893
      txio.setMultisig(isMulti);
 
1894
      insertTxio(txio);
 
1895
   }
 
1896
}
 
1897
 
 
1898
////////////////////////////////////////////////////////////////////////////////
 
1899
void StoredSubHistory::serializeDBValue(BinaryWriter & bw ) const
 
1900
{
 
1901
   bw.put_var_int(txioSet_.size());
 
1902
   map<BinaryData, TxIOPair>::const_iterator iter;
 
1903
   for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
 
1904
   {
 
1905
      TxIOPair const & txio = iter->second;
 
1906
      bool isSpent = txio.hasTxInInMain();
 
1907
 
 
1908
      // If spent and only maintaining a pruned DB, skip it
 
1909
      if(isSpent)
 
1910
      {
 
1911
         if(DBUtils.getDbPruneType()==DB_PRUNE_ALL)
 
1912
            continue;
 
1913
 
 
1914
         if(!txio.getTxRefOfInput().isInitialized())
 
1915
         {
 
1916
            LOGERR << "TxIO is spent, but input is not initialized";
 
1917
            continue;
 
1918
         }
 
1919
      }
 
1920
 
 
1921
      // We need to write
 
1922
      BinaryData key8B = txio.getDBKeyOfOutput();
 
1923
      if(!key8B.startsWith(hgtX_))
 
1924
         LOGERR << "How did TxIO key not match hgtX_??";
 
1925
 
 
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);
 
1932
 
 
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));
 
1936
 
 
1937
      // If not supposed to write the TxIn, we would've bailed earlier
 
1938
      if(isSpent)
 
1939
         bw.put_BinaryData(txio.getDBKeyOfInput());
 
1940
   }
 
1941
}
 
1942
 
 
1943
////////////////////////////////////////////////////////////////////////////////
 
1944
void StoredSubHistory::unserializeDBValue(BinaryData const & bd)
 
1945
{
 
1946
   BinaryRefReader brr(bd);
 
1947
   unserializeDBValue(brr);
 
1948
}
 
1949
 
 
1950
////////////////////////////////////////////////////////////////////////////////
 
1951
void StoredSubHistory::unserializeDBValue(BinaryDataRef bdr)
 
1952
{
 
1953
   BinaryRefReader brr(bdr);
 
1954
   unserializeDBValue(brr);
 
1955
}
 
1956
 
 
1957
////////////////////////////////////////////////////////////////////////////////
 
1958
BinaryData StoredSubHistory::serializeDBValue(void) const
 
1959
{
 
1960
   BinaryWriter bw;
 
1961
   serializeDBValue(bw);
 
1962
   return bw.getData();
 
1963
}
 
1964
 
 
1965
////////////////////////////////////////////////////////////////////////////////
 
1966
void StoredSubHistory::unserializeDBKey(BinaryDataRef key, bool withPrefix)
 
1967
{
 
1968
   uint32_t sz = key.getSize();
 
1969
   BinaryRefReader brr(key);
 
1970
   
 
1971
   // Assume prefix
 
1972
   if(withPrefix)
 
1973
   {
 
1974
      DBUtils.checkPrefixByte(brr, DB_PREFIX_SCRIPT);
 
1975
      sz -= 1;
 
1976
   }
 
1977
 
 
1978
   brr.get_BinaryData(uniqueKey_, sz-4);
 
1979
   brr.get_BinaryData(hgtX_, 4);
 
1980
}
 
1981
 
 
1982
////////////////////////////////////////////////////////////////////////////////
 
1983
BinaryData StoredSubHistory::getDBKey(bool withPrefix) const
 
1984
{
 
1985
   BinaryWriter bw;
 
1986
   if(withPrefix)
 
1987
      bw.put_uint8_t(DB_PREFIX_SCRIPT);
 
1988
 
 
1989
   bw.put_BinaryData(uniqueKey_);
 
1990
   bw.put_BinaryData(hgtX_);
 
1991
   return bw.getData();
 
1992
}
 
1993
 
 
1994
////////////////////////////////////////////////////////////////////////////////
 
1995
SCRIPT_PREFIX StoredSubHistory::getScriptType(void) const
 
1996
{
 
1997
   if(uniqueKey_.getSize() == 0)
 
1998
      return SCRIPT_PREFIX_NONSTD;
 
1999
   else
 
2000
      return (SCRIPT_PREFIX)uniqueKey_[0];
 
2001
}
 
2002
 
 
2003
////////////////////////////////////////////////////////////////////////////////
 
2004
void StoredSubHistory::pprintFullSubSSH(uint32_t indent)
 
2005
{
 
2006
   for(uint32_t ind=0; ind<indent; ind++)
 
2007
      cout << " ";
 
2008
 
 
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;
 
2013
 
 
2014
   // Print all the txioVects
 
2015
   map<BinaryData, TxIOPair>::iterator iter;
 
2016
   for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
 
2017
   {
 
2018
      for(uint32_t ind=0; ind<indent+3; ind++)
 
2019
         cout << " ";
 
2020
 
 
2021
      TxIOPair & txio = iter->second;
 
2022
      uint32_t hgt;
 
2023
      uint8_t  dup;
 
2024
      uint16_t txi;
 
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 << ")";
 
2031
 
 
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();
 
2038
 
 
2039
      if(txio.hasTxIn())
 
2040
      {
 
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 << ")";
 
2047
      }
 
2048
      cout << endl;
 
2049
   }
 
2050
   
 
2051
 
 
2052
}
 
2053
 
 
2054
////////////////////////////////////////////////////////////////////////////////
 
2055
TxIOPair* StoredSubHistory::findTxio(BinaryData const & dbKey8B, bool withMulti)
 
2056
{
 
2057
   map<BinaryData, TxIOPair>::iterator iter = txioSet_.find(dbKey8B);
 
2058
   if(ITER_NOT_IN_MAP(iter, txioSet_))
 
2059
      return NULL;
 
2060
   else
 
2061
   {
 
2062
      if(!withMulti && iter->second.isMultisig())
 
2063
         return NULL;
 
2064
 
 
2065
      return &(iter->second);
 
2066
   }
 
2067
}
 
2068
 
 
2069
////////////////////////////////////////////////////////////////////////////////
 
2070
TxIOPair& StoredSubHistory::insertTxio(TxIOPair const & txio, bool withOverwrite)
 
2071
{
 
2072
   BinaryData key8B = txio.getDBKeyOfOutput();
 
2073
   if(!key8B.startsWith(hgtX_))
 
2074
   {
 
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
 
2078
   }
 
2079
      
 
2080
   pair<BinaryData, TxIOPair> txioInsertPair(txio.getDBKeyOfOutput(), txio);
 
2081
   pair<map<BinaryData, TxIOPair>::iterator, bool> txioInsertResult;
 
2082
 
 
2083
   // This returns pair<ExistingOrInsertedIter, wasInserted>
 
2084
   txioInsertResult = txioSet_.insert(txioInsertPair);
 
2085
 
 
2086
   // If not inserted, then it was already there.  Overwrite if requested
 
2087
   if(!txioInsertResult.second && withOverwrite)
 
2088
      txioInsertResult.first->second = txio;
 
2089
 
 
2090
   return txioInsertResult.first->second;
 
2091
 
 
2092
}
 
2093
 
 
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)
 
2099
{
 
2100
   return eraseTxio(txio.getDBKeyOfOutput());
 
2101
}
 
2102
 
 
2103
////////////////////////////////////////////////////////////////////////////////
 
2104
uint64_t StoredSubHistory::eraseTxio(BinaryData const & dbKey8B)
 
2105
{
 
2106
   map<BinaryData, TxIOPair>::iterator iter = txioSet_.find(dbKey8B);
 
2107
   if(ITER_NOT_IN_MAP(iter, txioSet_))
 
2108
      return UINT64_MAX;
 
2109
   else
 
2110
   {
 
2111
      TxIOPair & txio = iter->second;
 
2112
      uint64_t valueRemoved = txio.getValue();
 
2113
      if(txio.hasTxInInMain() || txio.isMultisig())
 
2114
         valueRemoved = 0;
 
2115
 
 
2116
      txioSet_.erase(iter);
 
2117
      return valueRemoved;
 
2118
   }
 
2119
}
 
2120
 
 
2121
 
 
2122
////////////////////////////////////////////////////////////////////////////////
 
2123
uint64_t StoredSubHistory::getSubHistoryReceived(bool withMultisig)
 
2124
{
 
2125
   uint64_t bal = 0;
 
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();
 
2130
   
 
2131
   return bal;
 
2132
}
 
2133
 
 
2134
////////////////////////////////////////////////////////////////////////////////
 
2135
uint64_t StoredSubHistory::getSubHistoryBalance(bool withMultisig)
 
2136
{
 
2137
   uint64_t bal = 0;
 
2138
   map<BinaryData, TxIOPair>::iterator iter;
 
2139
   for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
 
2140
   {
 
2141
      if(!iter->second.hasTxIn())
 
2142
         if(!iter->second.isMultisig() || withMultisig)
 
2143
            bal += iter->second.getValue();
 
2144
   }
 
2145
   return bal;
 
2146
}
 
2147
 
 
2148
////////////////////////////////////////////////////////////////////////////////
 
2149
uint64_t StoredSubHistory::markTxOutSpent(BinaryData txOutKey8B, 
 
2150
                                                BinaryData txInKey8B)
 
2151
{
 
2152
   // We found the TxIO we care about 
 
2153
   if(DBUtils.getDbPruneType() != DB_PRUNE_NONE)
 
2154
   {
 
2155
      LOGERR << "Have not yet implemented pruning logic yet!";
 
2156
      return UINT64_MAX;
 
2157
   }
 
2158
 
 
2159
   TxIOPair * txioptr = findTxio(txOutKey8B);
 
2160
   if(txioptr==NULL)
 
2161
   {
 
2162
      LOGERR << "We should've found an STXO in the SSH but didn't";
 
2163
      return UINT64_MAX;
 
2164
   }
 
2165
 
 
2166
   if(txioptr->hasTxInInMain())
 
2167
   {
 
2168
      LOGWARN << "TxOut is already marked as spent";
 
2169
      return 0;
 
2170
   }
 
2171
 
 
2172
   if(txInKey8B.getSize() != 8)
 
2173
   {
 
2174
      LOGERR << "TxIn key input not valid! " << txInKey8B.toHexStr();
 
2175
      return UINT64_MAX;
 
2176
   }
 
2177
 
 
2178
   txioptr->setTxIn(txInKey8B);
 
2179
 
 
2180
   // Return value spent only if not multisig
 
2181
   return (txioptr->isMultisig() ? 0 : txioptr->getValue());
 
2182
}
 
2183
 
 
2184
 
 
2185
 
 
2186
////////////////////////////////////////////////////////////////////////////////
 
2187
// This method will add the TxIOPair to the SSH object if it doesn't exist,
 
2188
// in addition to marking it unspent.  
 
2189
//
 
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:
 
2192
//
 
2193
//    Key:                       Value:
 
2194
//    -------                    -------
 
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
 
2199
//    
 
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.
 
2205
//   
 
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, 
 
2209
                                                  uint64_t   value,
 
2210
                                                  bool       isCoinbase,
 
2211
                                                  bool       isMultisigRef)
 
2212
{
 
2213
   TxIOPair* txioptr = findTxio(txOutKey8B);
 
2214
   if(txioptr != NULL)
 
2215
   {
 
2216
      if(DBUtils.getDbPruneType() != DB_PRUNE_NONE)
 
2217
      {
 
2218
         LOGERR << "Found STXO that we expected to already be pruned...";
 
2219
         return 0;
 
2220
      }
 
2221
 
 
2222
      if(!txioptr->hasTxInInMain())
 
2223
      {
 
2224
         LOGWARN << "STXO already marked unspent in SSH";
 
2225
         return 0;
 
2226
      }
 
2227
 
 
2228
      txioptr->setTxIn(TxRef(), UINT32_MAX);
 
2229
      return (txioptr->isMultisig() ? 0 : txioptr->getValue());
 
2230
   }
 
2231
   else
 
2232
   {
 
2233
      if(value==UINT64_MAX)
 
2234
      {
 
2235
         LOGERR << "Need to add TxOut to sub-history, but no value supplied!";
 
2236
         return UINT64_MAX;
 
2237
      }
 
2238
   
 
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
 
2244
      insertTxio(txio);
 
2245
      return (isMultisigRef ? 0 : value);
 
2246
   }
 
2247
}
 
2248
 
 
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)
 
2257
{
 
2258
   //
 
2259
   vector<uint64_t> values(4);
 
2260
   for(uint32_t i=0; i<4; i++)
 
2261
      values[i] = 0;
 
2262
 
 
2263
   map<BinaryData, TxIOPair>::iterator iter;
 
2264
   for(iter = txioSet_.begin(); iter != txioSet_.end(); iter++)
 
2265
   {
 
2266
      if(iter->second.isMultisig())
 
2267
      {
 
2268
         
 
2269
      }
 
2270
      if(!iter->second.hasTxIn())
 
2271
         if(!iter->second.isMultisig() || withMultisig)
 
2272
            bal += iter->second.getValue();
 
2273
   }
 
2274
   return bal;
 
2275
}
 
2276
*/
 
2277
 
 
2278
////////////////////////////////////////////////////////////////////////////////
 
2279
////////////////////////////////////////////////////////////////////////////////
 
2280
void StoredUndoData::unserializeDBValue(BinaryRefReader & brr)
 
2281
{
 
2282
   brr.get_BinaryData(blockHash_, 32);
 
2283
 
 
2284
   uint32_t nStxoRmd = brr.get_uint32_t();
 
2285
   stxOutsRemovedByBlock_.clear();
 
2286
   stxOutsRemovedByBlock_.resize(nStxoRmd);
 
2287
 
 
2288
   for(uint32_t i=0; i<nStxoRmd; i++)
 
2289
   {
 
2290
      StoredTxOut & stxo = stxOutsRemovedByBlock_[i];
 
2291
 
 
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();
 
2297
 
 
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);
 
2303
 
 
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();
 
2309
   
 
2310
      // Then read the raw TxOut itself
 
2311
      stxo.unserialize(brr);
 
2312
   }
 
2313
 
 
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);
 
2319
    
 
2320
}
 
2321
 
 
2322
////////////////////////////////////////////////////////////////////////////////
 
2323
void StoredUndoData::serializeDBValue(BinaryWriter & bw ) const
 
2324
{
 
2325
   bw.put_BinaryData(blockHash_);
 
2326
 
 
2327
   uint32_t nStxoRmd = stxOutsRemovedByBlock_.size();
 
2328
   uint32_t nOpAdded = outPointsAddedByBlock_.size();
 
2329
 
 
2330
 
 
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++)
 
2335
   {
 
2336
      StoredTxOut const & stxo = stxOutsRemovedByBlock_[i];
 
2337
 
 
2338
      if(stxo.parentHash_.getSize() == 0          || 
 
2339
         stxo.txOutIndex_           == UINT16_MAX   )
 
2340
      {
 
2341
         LOGERR << "Can't write undo data w/o parent hash and/or TxOut index";
 
2342
         return;
 
2343
      }
 
2344
 
 
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_);
 
2350
 
 
2351
      bw.put_BitPacker(bitpack);
 
2352
 
 
2353
      // Put the blkdata key directly into the DB to save us a lookup 
 
2354
      bw.put_BinaryData( DBUtils.getBlkDataKeyNoPrefix( stxo.blockHeight_,
 
2355
                                                        stxo.duplicateID_,
 
2356
                                                        stxo.txIndex_,
 
2357
                                                        stxo.txOutIndex_));
 
2358
 
 
2359
      bw.put_BinaryData(stxo.parentHash_);
 
2360
      bw.put_uint32_t((uint32_t)stxo.txOutIndex_);
 
2361
      bw.put_BinaryData(stxo.getSerializedTxOut());
 
2362
   }
 
2363
 
 
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()); 
 
2372
}
 
2373
 
 
2374
////////////////////////////////////////////////////////////////////////////////
 
2375
void StoredUndoData::unserializeDBValue(BinaryData const & bd)
 
2376
{
 
2377
   BinaryRefReader brr(bd);
 
2378
   unserializeDBValue(brr);
 
2379
}
 
2380
 
 
2381
////////////////////////////////////////////////////////////////////////////////
 
2382
void StoredUndoData::unserializeDBValue(BinaryDataRef bdr)
 
2383
                                  
 
2384
{
 
2385
   BinaryRefReader brr(bdr);
 
2386
   unserializeDBValue(brr);
 
2387
}
 
2388
 
 
2389
////////////////////////////////////////////////////////////////////////////////
 
2390
BinaryData StoredUndoData::serializeDBValue(void) const
 
2391
{
 
2392
   BinaryWriter bw;
 
2393
   serializeDBValue(bw);
 
2394
   return bw.getData();
 
2395
}
 
2396
 
 
2397
////////////////////////////////////////////////////////////////////////////////
 
2398
BinaryData StoredUndoData::getDBKey(bool withPrefix) const
 
2399
{
 
2400
   if(!withPrefix)
 
2401
      return DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_);
 
2402
   else
 
2403
   {
 
2404
      BinaryWriter bw(5);
 
2405
      bw.put_uint8_t((uint8_t)DB_PREFIX_UNDODATA); 
 
2406
      bw.put_BinaryData( DBUtils.getBlkDataKeyNoPrefix(blockHeight_, duplicateID_));
 
2407
      return bw.getData();
 
2408
   }
 
2409
}
 
2410
 
 
2411
 
 
2412
////////////////////////////////////////////////////////////////////////////////
 
2413
////////////////////////////////////////////////////////////////////////////////
 
2414
void StoredTxHints::unserializeDBValue(BinaryRefReader & brr)
 
2415
{
 
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);
 
2420
 
 
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
 
2423
   if(numHints > 0)
 
2424
      preferredDBKey_ = dbKeyList_[0];
 
2425
}
 
2426
 
 
2427
 
 
2428
////////////////////////////////////////////////////////////////////////////////
 
2429
void StoredTxHints::serializeDBValue(BinaryWriter & bw ) const
 
2430
{
 
2431
   bw.put_var_int(dbKeyList_.size());
 
2432
   
 
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
 
2435
   // for sure...)
 
2436
   for(uint32_t i=0; i<dbKeyList_.size(); i++)
 
2437
   {
 
2438
      if(dbKeyList_[i] != preferredDBKey_)
 
2439
         continue;
 
2440
 
 
2441
      bw.put_BinaryData(dbKeyList_[i]);
 
2442
      break;
 
2443
   }
 
2444
 
 
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++)
 
2448
   {
 
2449
      if(dbKeyList_[i] == preferredDBKey_)
 
2450
         continue;
 
2451
 
 
2452
      bw.put_BinaryData(dbKeyList_[i]);
 
2453
   }
 
2454
}
 
2455
 
 
2456
////////////////////////////////////////////////////////////////////////////////
 
2457
void StoredTxHints::unserializeDBValue(BinaryData const & bd)
 
2458
{
 
2459
   BinaryRefReader brr(bd);
 
2460
   unserializeDBValue(brr);
 
2461
}
 
2462
 
 
2463
////////////////////////////////////////////////////////////////////////////////
 
2464
void StoredTxHints::unserializeDBValue(BinaryDataRef bdr)
 
2465
                                  
 
2466
{
 
2467
   BinaryRefReader brr(bdr);
 
2468
   unserializeDBValue(brr);
 
2469
}
 
2470
 
 
2471
////////////////////////////////////////////////////////////////////////////////
 
2472
BinaryData StoredTxHints::serializeDBValue(void) const
 
2473
{
 
2474
   BinaryWriter bw;
 
2475
   serializeDBValue(bw);
 
2476
   return bw.getData();
 
2477
}
 
2478
 
 
2479
 
 
2480
////////////////////////////////////////////////////////////////////////////////
 
2481
BinaryData StoredTxHints::getDBKey(bool withPrefix) const
 
2482
{
 
2483
   if(!withPrefix)
 
2484
      return txHashPrefix_;
 
2485
   else
 
2486
   {
 
2487
      BinaryWriter bw(5);
 
2488
      bw.put_uint8_t((uint8_t)DB_PREFIX_TXHINTS); 
 
2489
      bw.put_BinaryData( txHashPrefix_);
 
2490
      return bw.getData();
 
2491
   }
 
2492
}
 
2493
 
 
2494
////////////////////////////////////////////////////////////////////////////////
 
2495
void StoredTxHints::unserializeDBKey(BinaryDataRef key, bool withPrefix)
 
2496
{
 
2497
   if(withPrefix)
 
2498
      txHashPrefix_ = key.getSliceCopy(1, 4);
 
2499
   else
 
2500
      txHashPrefix_ = key;
 
2501
}
 
2502
 
 
2503
////////////////////////////////////////////////////////////////////////////////
 
2504
////////////////////////////////////////////////////////////////////////////////
 
2505
void StoredHeadHgtList::unserializeDBValue(BinaryRefReader & brr)
 
2506
{
 
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++)
 
2511
   {
 
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;
 
2517
   }
 
2518
}
 
2519
 
 
2520
////////////////////////////////////////////////////////////////////////////////
 
2521
void StoredHeadHgtList::serializeDBValue(BinaryWriter & bw ) const
 
2522
{
 
2523
   bw.put_uint8_t( dupAndHashList_.size() );
 
2524
 
 
2525
   // Write the preferred/valid block header first
 
2526
   for(uint32_t i=0; i<dupAndHashList_.size(); i++)
 
2527
   {
 
2528
      if(dupAndHashList_[i].first != preferredDup_)
 
2529
         continue; 
 
2530
 
 
2531
      bw.put_uint8_t(dupAndHashList_[i].first | 0x80);
 
2532
      bw.put_BinaryData(dupAndHashList_[i].second);
 
2533
      break;
 
2534
   }
 
2535
         
 
2536
   // Now write everything else
 
2537
   for(uint32_t i=0; i<dupAndHashList_.size(); i++)
 
2538
   {
 
2539
      if(dupAndHashList_[i].first == preferredDup_)
 
2540
         continue; 
 
2541
 
 
2542
      bw.put_uint8_t(dupAndHashList_[i].first & 0x7f);
 
2543
      bw.put_BinaryData(dupAndHashList_[i].second);
 
2544
   }
 
2545
 
 
2546
}
 
2547
 
 
2548
////////////////////////////////////////////////////////////////////////////////
 
2549
void StoredHeadHgtList::unserializeDBValue(BinaryData const & bd)
 
2550
{
 
2551
   BinaryRefReader brr(bd);
 
2552
   unserializeDBValue(brr);
 
2553
}
 
2554
 
 
2555
////////////////////////////////////////////////////////////////////////////////
 
2556
void StoredHeadHgtList::unserializeDBValue(BinaryDataRef bdr)
 
2557
                                  
 
2558
{
 
2559
   BinaryRefReader brr(bdr);
 
2560
   unserializeDBValue(brr);
 
2561
}
 
2562
 
 
2563
////////////////////////////////////////////////////////////////////////////////
 
2564
BinaryData StoredHeadHgtList::serializeDBValue(void) const
 
2565
{
 
2566
   BinaryWriter bw;
 
2567
   serializeDBValue(bw);
 
2568
   return bw.getData();
 
2569
}
 
2570
 
 
2571
////////////////////////////////////////////////////////////////////////////////
 
2572
BinaryData StoredHeadHgtList::getDBKey(bool withPrefix) const
 
2573
{
 
2574
   BinaryWriter bw(5);
 
2575
   if(withPrefix)
 
2576
      bw.put_uint8_t((uint8_t)DB_PREFIX_HEADHGT); 
 
2577
   bw.put_uint32_t(height_, BIGENDIAN);
 
2578
   return bw.getData();
 
2579
 
 
2580
}
 
2581
 
 
2582
////////////////////////////////////////////////////////////////////////////////
 
2583
void StoredHeadHgtList::unserializeDBKey(BinaryDataRef key)
 
2584
{
 
2585
   BinaryRefReader brr(key);
 
2586
   if(key.getSize() == 5)
 
2587
   {
 
2588
      uint8_t prefix = brr.get_uint8_t();
 
2589
      if(prefix != DB_PREFIX_HEADHGT)  
 
2590
      {
 
2591
         LOGERR << "Unserialized HEADHGT key but wrong prefix";
 
2592
         return;
 
2593
      }
 
2594
   }
 
2595
 
 
2596
   height_ = brr.get_uint32_t(BIGENDIAN);
 
2597
}
 
2598
 
 
2599
 
 
2600
////////////////////////////////////////////////////////////////////////////////
 
2601
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKey( BinaryRefReader & brr,
 
2602
                                                uint32_t & height,
 
2603
                                                uint8_t  & dupID)
 
2604
{
 
2605
   uint16_t tempTxIdx;
 
2606
   uint16_t tempTxOutIdx;
 
2607
   return readBlkDataKey(brr, height, dupID, tempTxIdx, tempTxOutIdx);
 
2608
}
 
2609
 
 
2610
////////////////////////////////////////////////////////////////////////////////
 
2611
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKey( BinaryRefReader & brr,
 
2612
                                                uint32_t & height,
 
2613
                                                uint8_t  & dupID,
 
2614
                                                uint16_t & txIdx)
 
2615
{
 
2616
   uint16_t tempTxOutIdx;
 
2617
   return readBlkDataKey(brr, height, dupID, txIdx, tempTxOutIdx);
 
2618
}
 
2619
 
 
2620
////////////////////////////////////////////////////////////////////////////////
 
2621
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKey( BinaryRefReader & brr,
 
2622
                                                uint32_t & height,
 
2623
                                                uint8_t  & dupID,
 
2624
                                                uint16_t & txIdx,
 
2625
                                                uint16_t & txOutIdx)
 
2626
{
 
2627
   uint8_t prefix = brr.get_uint8_t();
 
2628
   if(prefix != (uint8_t)DB_PREFIX_TXDATA)
 
2629
   {
 
2630
      height   = 0xffffffff;
 
2631
      dupID    =       0xff;
 
2632
      txIdx    =     0xffff;
 
2633
      txOutIdx =     0xffff;
 
2634
      return NOT_BLKDATA;
 
2635
   }
 
2636
   
 
2637
   return readBlkDataKeyNoPrefix(brr, height, dupID, txIdx, txOutIdx);
 
2638
}
 
2639
 
 
2640
////////////////////////////////////////////////////////////////////////////////
 
2641
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKeyNoPrefix(
 
2642
                                       BinaryRefReader & brr,
 
2643
                                       uint32_t & height,
 
2644
                                       uint8_t  & dupID)
 
2645
{
 
2646
   uint16_t tempTxIdx;
 
2647
   uint16_t tempTxOutIdx;
 
2648
   return readBlkDataKeyNoPrefix(brr, height, dupID, tempTxIdx, tempTxOutIdx);
 
2649
}
 
2650
 
 
2651
////////////////////////////////////////////////////////////////////////////////
 
2652
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKeyNoPrefix(
 
2653
                                       BinaryRefReader & brr,
 
2654
                                       uint32_t & height,
 
2655
                                       uint8_t  & dupID,
 
2656
                                       uint16_t & txIdx)
 
2657
{
 
2658
   uint16_t tempTxOutIdx;
 
2659
   return readBlkDataKeyNoPrefix(brr, height, dupID, txIdx, tempTxOutIdx);
 
2660
}
 
2661
 
 
2662
////////////////////////////////////////////////////////////////////////////////
 
2663
BLKDATA_TYPE GlobalDBUtilities::readBlkDataKeyNoPrefix(
 
2664
                                       BinaryRefReader & brr,
 
2665
                                       uint32_t & height,
 
2666
                                       uint8_t  & dupID,
 
2667
                                       uint16_t & txIdx,
 
2668
                                       uint16_t & txOutIdx)
 
2669
{
 
2670
   BinaryData hgtx = brr.get_BinaryData(4);
 
2671
   height = hgtxToHeight(hgtx);
 
2672
   dupID  = hgtxToDupID(hgtx);
 
2673
 
 
2674
   if(brr.getSizeRemaining() == 0)
 
2675
   {
 
2676
      txIdx    = 0xffff;
 
2677
      txOutIdx = 0xffff;
 
2678
      return BLKDATA_HEADER;
 
2679
   }
 
2680
   else if(brr.getSizeRemaining() == 2)
 
2681
   {
 
2682
      txIdx    = brr.get_uint16_t(BIGENDIAN);
 
2683
      txOutIdx = 0xffff;
 
2684
      return BLKDATA_TX;
 
2685
   }
 
2686
   else if(brr.getSizeRemaining() == 4)
 
2687
   {
 
2688
      txIdx    = brr.get_uint16_t(BIGENDIAN);
 
2689
      txOutIdx = brr.get_uint16_t(BIGENDIAN);
 
2690
      return BLKDATA_TXOUT;
 
2691
   }
 
2692
   else
 
2693
   {
 
2694
      LOGERR << "Unexpected bytes remaining: " << brr.getSizeRemaining();
 
2695
      return NOT_BLKDATA;
 
2696
   }
 
2697
}
 
2698
 
 
2699
 
 
2700
 
 
2701
 
 
2702
 
 
2703
////////////////////////////////////////////////////////////////////////////////
 
2704
////////////////////////////////////////////////////////////////////////////////
 
2705
string GlobalDBUtilities::getPrefixName(uint8_t prefixInt)
 
2706
{
 
2707
   return getPrefixName((DB_PREFIX)prefixInt);
 
2708
}
 
2709
 
 
2710
////////////////////////////////////////////////////////////////////////////////
 
2711
string GlobalDBUtilities::getPrefixName(DB_PREFIX pref)
 
2712
{
 
2713
   switch(pref)
 
2714
   {
 
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>"); 
 
2724
   }
 
2725
}
 
2726
 
 
2727
/////////////////////////////////////////////////////////////////////////////
 
2728
bool GlobalDBUtilities::checkPrefixByteWError( BinaryRefReader & brr, 
 
2729
                                               DB_PREFIX prefix,
 
2730
                                               bool rewindWhenDone)
 
2731
{
 
2732
   uint8_t oneByte = brr.get_uint8_t();
 
2733
   bool out;
 
2734
   if(oneByte == (uint8_t)prefix)
 
2735
      out = true;
 
2736
   else
 
2737
   {
 
2738
      LOGERR << "Unexpected prefix byte: "
 
2739
                 << "Expected: " << getPrefixName(prefix)
 
2740
                 << "Received: " << getPrefixName(oneByte);
 
2741
      out = false;
 
2742
   }
 
2743
 
 
2744
   if(rewindWhenDone)
 
2745
      brr.rewind(1);
 
2746
 
 
2747
   return out;
 
2748
}
 
2749
 
 
2750
/////////////////////////////////////////////////////////////////////////////
 
2751
bool GlobalDBUtilities::checkPrefixByte( BinaryRefReader & brr, 
 
2752
                                          DB_PREFIX prefix,
 
2753
                                          bool rewindWhenDone)
 
2754
{
 
2755
   uint8_t oneByte = brr.get_uint8_t();
 
2756
   bool out = (oneByte == (uint8_t)prefix);
 
2757
 
 
2758
   if(rewindWhenDone)
 
2759
      brr.rewind(1);
 
2760
 
 
2761
   return out;
 
2762
}
 
2763
 
 
2764
/////////////////////////////////////////////////////////////////////////////
 
2765
BinaryData GlobalDBUtilities::getBlkDataKey( uint32_t height, 
 
2766
                                             uint8_t  dup)
 
2767
{
 
2768
   BinaryWriter bw(5);
 
2769
   bw.put_uint8_t(    DB_PREFIX_TXDATA );
 
2770
   bw.put_BinaryData( heightAndDupToHgtx(height,dup) );
 
2771
   return bw.getData();
 
2772
}
 
2773
 
 
2774
/////////////////////////////////////////////////////////////////////////////
 
2775
BinaryData GlobalDBUtilities::getBlkDataKey( uint32_t height, 
 
2776
                                             uint8_t  dup,
 
2777
                                             uint16_t txIdx)
 
2778
{
 
2779
   BinaryWriter bw(7);
 
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();
 
2784
}
 
2785
 
 
2786
/////////////////////////////////////////////////////////////////////////////
 
2787
BinaryData GlobalDBUtilities::getBlkDataKey(uint32_t height, 
 
2788
                                            uint8_t  dup,
 
2789
                                            uint16_t txIdx,
 
2790
                                            uint16_t txOutIdx)
 
2791
{
 
2792
   BinaryWriter bw(9);
 
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();
 
2798
}
 
2799
 
 
2800
/////////////////////////////////////////////////////////////////////////////
 
2801
BinaryData GlobalDBUtilities::getBlkDataKeyNoPrefix( uint32_t height, 
 
2802
                                                     uint8_t  dup)
 
2803
{
 
2804
   return heightAndDupToHgtx(height,dup);
 
2805
}
 
2806
 
 
2807
/////////////////////////////////////////////////////////////////////////////
 
2808
BinaryData GlobalDBUtilities::getBlkDataKeyNoPrefix( uint32_t height, 
 
2809
                                                     uint8_t  dup,
 
2810
                                                     uint16_t txIdx)
 
2811
{
 
2812
   BinaryWriter bw(6);
 
2813
   bw.put_BinaryData( heightAndDupToHgtx(height,dup));
 
2814
   bw.put_uint16_t(   txIdx, BIGENDIAN);
 
2815
   return bw.getData();
 
2816
}
 
2817
 
 
2818
/////////////////////////////////////////////////////////////////////////////
 
2819
BinaryData GlobalDBUtilities::getBlkDataKeyNoPrefix( uint32_t height, 
 
2820
                                                     uint8_t  dup,
 
2821
                                                     uint16_t txIdx,
 
2822
                                                     uint16_t txOutIdx)
 
2823
{
 
2824
   BinaryWriter bw(8);
 
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();
 
2829
}
 
2830
 
 
2831
/////////////////////////////////////////////////////////////////////////////
 
2832
uint32_t GlobalDBUtilities::hgtxToHeight(const BinaryData& hgtx)
 
2833
{
 
2834
   return (READ_UINT32_BE(hgtx) >> 8);
 
2835
 
 
2836
}
 
2837
 
 
2838
/////////////////////////////////////////////////////////////////////////////
 
2839
uint8_t GlobalDBUtilities::hgtxToDupID(const BinaryData& hgtx)
 
2840
{
 
2841
   return (READ_UINT32_BE(hgtx) & 0x7f);
 
2842
}
 
2843
 
 
2844
/////////////////////////////////////////////////////////////////////////////
 
2845
BinaryData GlobalDBUtilities::heightAndDupToHgtx(uint32_t hgt, uint8_t dup)
 
2846
{
 
2847
   uint32_t hgtxInt = (hgt<<8) | (uint32_t)dup;
 
2848
   return WRITE_UINT32_BE(hgtxInt);
 
2849
}
 
2850
 
 
2851
// kate: indent-width 3; replace-tabs on;