Esempio n. 1
0
 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
Esempio n. 2
0
 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
Esempio n. 3
0
 def get_txnums(self, hashX, limit=1000):
     '''Generator that returns an unpruned, sorted list of tx_nums in the
     history of a hashX.  Includes both spending and receiving
     transactions.  By default yields at most 1000 entries.  Set
     limit to None to get them all.  '''
     limit = util.resolve_limit(limit)
     chunks = util.chunks
     for _key, hist in self.db.iterator(prefix=hashX):
         for tx_numb in chunks(hist, 5):
             if limit == 0:
                 return
             tx_num, = unpack_le_uint64(tx_numb + bytes(3))
             yield tx_num
             limit -= 1
Esempio n. 4
0
            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[-5:]
                    tx_num, = unpack_le_uint64(tx_num_packed + bytes(3))
                    fs_hash, _height = self.fs_tx_hash(tx_num)
                    if fs_hash == tx_hash:
                        return hashX, idx_packed + tx_num_packed
                return None, None
Esempio n. 5
0
 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
Esempio n. 6
0
 def lookup_utxo(hashX, suffix):
     if not hashX:
         # This can happen when the daemon is a block ahead
         # of us and has mempool txs spending outputs from
         # that new block
         return None
     # Key: b'u' + address_hashX + tx_idx + tx_num
     # Value: the UTXO value as a 64-bit unsigned integer
     key = b'u' + hashX + suffix
     db_value = self.utxo_db.get(key)
     if not db_value:
         # This can happen if the DB was updated between
         # getting the hashXs and getting the UTXOs
         return None
     value, = unpack_le_uint64(db_value)
     return hashX, value
Esempio n. 7
0
    def spend_utxo(self, tx_hash: bytes, tx_idx: int, spend_tx_hash) -> bytes:
        '''Spend a UTXO and return (hashX + tx_num + value_sats).

        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.
        txnum_padding = bytes(8-TXNUM_LEN)

        # Key: b'h' + compressed_tx_hash + tx_idx + tx_num
        # Value: hashX
        prefix = b'h' + tx_hash[:COMP_TXID_LEN] + 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[-TXNUM_LEN:]

            if len(candidates) > 1:
                tx_num, = unpack_le_uint64(tx_num_packed + txnum_padding)
                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[-4-TXNUM_LEN:]
            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(f'UTXO {hash_to_hex_str(tx_hash)} / {tx_idx:,d} not '
                         f'found in "h" table')
Esempio n. 8
0
    def spend_utxo(self, tx_hash, tx_idx):
        '''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[-5:]

            if len(candidates) > 1:
                tx_num, = unpack_le_uint64(tx_num_packed + bytes(3))
                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[-9:]
            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

        raise ChainError('UTXO {} / {:,d} not found in "h" table'.format(
            hash_to_hex_str(tx_hash), tx_idx))
Esempio n. 9
0
 def dynamic_header_offset(self, height):
     assert not self.coin.STATIC_BLOCK_HEADERS
     offset, = unpack_le_uint64(
         self.headers_offsets_file.read(height * 8, 8))
     return offset