def read_utxos(): utxos = [] utxos_append = utxos.append # Key: b'u' + address_hashX + tx_idx + tx_num # Value: the UTXO value as a 64-bit unsigned integer prefix = b'u' + hashX for db_key, db_value in self.utxo_db.iterator(prefix=prefix): tx_pos, = unpack_le_uint32(db_key[-9:-5]) tx_num, = unpack_le_uint64(db_key[-5:] + bytes(3)) value, = unpack_le_uint64(db_value) tx_hash, height = self.fs_tx_hash(tx_num) utxos_append(UTXO(tx_num, tx_pos, tx_hash, height, value)) return utxos
def read_utxos(): utxos = [] utxos_append = utxos.append txnum_padding = bytes(8-TXNUM_LEN) # Key: b'u' + address_hashX + txout_idx + tx_num # Value: the UTXO value as a 64-bit unsigned integer prefix = b'u' + hashX for db_key, db_value in self.utxo_db.iterator(prefix=prefix): txout_idx, = unpack_le_uint32(db_key[-TXNUM_LEN-4:-TXNUM_LEN]) tx_num, = unpack_le_uint64(db_key[-TXNUM_LEN:] + txnum_padding) value, = unpack_le_uint64(db_value) tx_hash, height = self.fs_tx_hash(tx_num) utxos_append(UTXO(tx_num, txout_idx, tx_hash, height, value)) return utxos
def spend_utxo(self, tx_hash, tx_idx, spend_tx_hash): '''Spend a UTXO and return the 33-byte value. If the UTXO is not in the cache it must be on disk. We store all UTXOs so not finding one indicates a logic error or DB corruption. ''' # Fast track is it being in the cache idx_packed = pack_le_uint32(tx_idx) cache_value = self.utxo_cache.pop(tx_hash + idx_packed, None) if cache_value: return cache_value # Spend it from the DB. # Key: b'h' + compressed_tx_hash + tx_idx + tx_num # Value: hashX prefix = b'h' + tx_hash[:4] + idx_packed candidates = { db_key: hashX for db_key, hashX in self.db.utxo_db.iterator(prefix=prefix) } for hdb_key, hashX in candidates.items(): tx_num_packed = hdb_key[-4:] if len(candidates) > 1: tx_num, = unpack_le_uint32(tx_num_packed) hash, _height = self.db.fs_tx_hash(tx_num) if hash != tx_hash: assert hash is not None # Should always be found continue # Key: b'u' + address_hashX + tx_idx + tx_num # Value: the UTXO value as a 64-bit unsigned integer udb_key = b'u' + hashX + hdb_key[-8:] utxo_value_packed = self.db.utxo_db.get(udb_key) if utxo_value_packed: # Remove both entries for this UTXO self.db_deletes.append(hdb_key) self.db_deletes.append(udb_key) return hashX + tx_num_packed + utxo_value_packed if spend_tx_hash in self.dup_tx_hashes: return None raise ChainError( 'UTXO {}:{:,d} not found in "h" table at height {}, spend tx {}'. format(hash_to_hex_str(tx_hash), tx_idx, self.height, hash_to_hex_str(spend_tx_hash)))
def lookup_hashX(tx_hash, tx_idx): idx_packed = pack_le_uint32(tx_idx) # Key: b'h' + compressed_tx_hash + tx_idx + tx_num # Value: hashX prefix = b'h' + tx_hash[:4] + idx_packed # Find which entry, if any, the TX_HASH matches. for db_key, hashX in self.utxo_db.iterator(prefix=prefix): tx_num_packed = db_key[-4:] tx_num, = unpack_le_uint32(tx_num_packed) hash, _height = self.fs_tx_hash(tx_num) if hash == tx_hash: return hashX, idx_packed + tx_num_packed return None, None
def read_utxos(): utxos = [] utxos_append = utxos.append # Key: b'u' + address_hashX + tx_num + txout_idx # Value: the UTXO value as a 64-bit unsigned integer prefix = b'u' + hashX for db_key, db_value in self.utxo_db.iterator(prefix=prefix): txout_idx, = unpack_le_uint32(db_key[-TXOUTIDX_LEN:] + TXOUTIDX_PADDING) tx_numb = db_key[-TXOUTIDX_LEN - TXNUM_LEN:-TXOUTIDX_LEN] tx_num = unpack_txnum(tx_numb) value, = unpack_le_uint64(db_value) tx_hash, height = self.fs_tx_hash(tx_num) utxos_append(UTXO(tx_num, txout_idx, tx_hash, height, value)) return utxos
def date_str(self): timestamp, = unpack_le_uint32(self.header[68:72]) return datetime.utcfromtimestamp(timestamp).strftime( '%Y-%m-%d %H:%M:%S')