Remove Outpoint as a separate object
Hopefully this is a little more efficient
This commit is contained in:
parent
6957b59b19
commit
2b45698962
47
lib/tx.py
47
lib/tx.py
@ -2,11 +2,10 @@
|
||||
# and warranty status of this software.
|
||||
|
||||
from collections import namedtuple
|
||||
import binascii
|
||||
import struct
|
||||
|
||||
from lib.util import cachedproperty
|
||||
from lib.hash import double_sha256
|
||||
from lib.hash import double_sha256, hash_to_str
|
||||
|
||||
|
||||
class Tx(namedtuple("Tx", "version inputs outputs locktime")):
|
||||
@ -15,17 +14,17 @@ class Tx(namedtuple("Tx", "version inputs outputs locktime")):
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase
|
||||
|
||||
OutPoint = namedtuple("OutPoint", "hash n")
|
||||
# FIXME: add hash as a cached property?
|
||||
|
||||
# prevout is an OutPoint object
|
||||
class TxInput(namedtuple("TxInput", "prevout script sequence")):
|
||||
class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")):
|
||||
|
||||
ZERO = bytes(32)
|
||||
MINUS_1 = 4294967295
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.prevout == (TxInput.ZERO, TxInput.MINUS_1)
|
||||
return (self.prev_hash == TxInput.ZERO
|
||||
and self.prev_idx == TxInput.MINUS_1)
|
||||
|
||||
@cachedproperty
|
||||
def script_sig_info(self):
|
||||
@ -34,11 +33,11 @@ class TxInput(namedtuple("TxInput", "prevout script sequence")):
|
||||
return None
|
||||
return Script.parse_script_sig(self.script)
|
||||
|
||||
def __repr__(self):
|
||||
script = binascii.hexlify(self.script).decode("ascii")
|
||||
prev_hash = binascii.hexlify(self.prevout.hash).decode("ascii")
|
||||
return ("Input(prevout=({}, {:d}), script={}, sequence={:d})"
|
||||
.format(prev_hash, self.prevout.n, script, self.sequence))
|
||||
def __str__(self):
|
||||
script = self.script.hex()
|
||||
prev_hash = hash_to_str(self.prev_hash)
|
||||
return ("Input({}, {:d}, script={}, sequence={:d})"
|
||||
.format(prev_hash, self.prev_idx, script, self.sequence))
|
||||
|
||||
|
||||
class TxOutput(namedtuple("TxOutput", "value pk_script")):
|
||||
@ -56,11 +55,12 @@ class Deserializer(object):
|
||||
self.cursor = 0
|
||||
|
||||
def read_tx(self):
|
||||
version = self.read_le_int32()
|
||||
inputs = self.read_inputs()
|
||||
outputs = self.read_outputs()
|
||||
locktime = self.read_le_uint32()
|
||||
return Tx(version, inputs, outputs, locktime)
|
||||
return Tx(
|
||||
self.read_le_int32(), # version
|
||||
self.read_inputs(), # inputs
|
||||
self.read_outputs(), # outputs
|
||||
self.read_le_uint32() # locktime
|
||||
)
|
||||
|
||||
def read_block(self):
|
||||
tx_hashes = []
|
||||
@ -81,15 +81,12 @@ class Deserializer(object):
|
||||
return [self.read_input() for i in range(n)]
|
||||
|
||||
def read_input(self):
|
||||
prevout = self.read_outpoint()
|
||||
script = self.read_varbytes()
|
||||
sequence = self.read_le_uint32()
|
||||
return TxInput(prevout, script, sequence)
|
||||
|
||||
def read_outpoint(self):
|
||||
hash = self.read_nbytes(32)
|
||||
n = self.read_le_uint32()
|
||||
return OutPoint(hash, n)
|
||||
return TxInput(
|
||||
self.read_nbytes(32), # prev_hash
|
||||
self.read_le_uint32(), # prev_idx
|
||||
self.read_varbytes(), # script
|
||||
self.read_le_uint32() # sequence
|
||||
)
|
||||
|
||||
def read_outputs(self):
|
||||
n = self.read_varint()
|
||||
|
||||
@ -439,7 +439,7 @@ class BlockProcessor(LoggedClass):
|
||||
hash168s = cache.add_many(tx_hash, tx_num, tx.outputs)
|
||||
if not tx.is_coinbase:
|
||||
for txin in tx.inputs:
|
||||
hash168s.add(cache.spend(txin.prevout))
|
||||
hash168s.add(cache.spend(txin))
|
||||
|
||||
for hash168 in hash168s:
|
||||
self.history[hash168].append(tx_num)
|
||||
|
||||
@ -120,21 +120,21 @@ class UTXOCache(LoggedClass):
|
||||
|
||||
return hash168s
|
||||
|
||||
def spend(self, prevout):
|
||||
def spend(self, txin):
|
||||
'''Spend a UTXO and return the address spent.
|
||||
|
||||
If the UTXO is not in the cache it must be on disk.
|
||||
'''
|
||||
# Fast track is it's in the cache
|
||||
pack = struct.pack
|
||||
key = prevout.hash + pack('<H', prevout.n)
|
||||
key = txin.prev_hash + pack('<H', txin.prev_idx)
|
||||
value = self.cache.pop(key, None)
|
||||
if value:
|
||||
self.cache_hits += 1
|
||||
return value[:21]
|
||||
|
||||
# Oh well. Find and remove it from the DB.
|
||||
hash168 = self.hash168(prevout.hash, prevout.n)
|
||||
hash168 = self.hash168(txin.prev_hash, txin.prev_idx)
|
||||
if not hash168:
|
||||
return None
|
||||
|
||||
@ -142,14 +142,14 @@ class UTXOCache(LoggedClass):
|
||||
|
||||
# Read the UTXO through the cache from the disk. We have to
|
||||
# go through the cache because compressed keys can collide.
|
||||
key = (b'u' + hash168 + prevout.hash[:UTXO_TX_HASH_LEN]
|
||||
+ pack('<H', prevout.n))
|
||||
key = (b'u' + hash168 + txin.prev_hash[:UTXO_TX_HASH_LEN]
|
||||
+ pack('<H', txin.prev_idx))
|
||||
data = self.cache_get(key)
|
||||
if data is None:
|
||||
# Uh-oh, this should not happen...
|
||||
self.logger.error('found no UTXO for {} / {:d} key {}'
|
||||
.format(hash_to_str(prevout.hash),
|
||||
prevout.n, bytes(key).hex()))
|
||||
.format(hash_to_str(txin.prev_hash),
|
||||
txin.prev_idx, bytes(key).hex()))
|
||||
return hash168
|
||||
|
||||
if len(data) == 12:
|
||||
@ -163,7 +163,7 @@ class UTXOCache(LoggedClass):
|
||||
for n in range(0, len(data), 12):
|
||||
(tx_num, ) = struct.unpack('<I', data[n:n+4])
|
||||
tx_hash, height = self.parent.get_tx_hash(tx_num)
|
||||
if prevout.hash == tx_hash:
|
||||
if txin.prev_hash == tx_hash:
|
||||
data = data[:n] + data[n + 12:]
|
||||
self.cache_write(key, data)
|
||||
return hash168
|
||||
|
||||
Loading…
Reference in New Issue
Block a user