Browse Source

Remove Outpoint as a separate object

Hopefully this is a little more efficient
master
Neil Booth 8 years ago
parent
commit
2b45698962
  1. 47
      lib/tx.py
  2. 2
      server/block_processor.py
  3. 16
      server/cache.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()

2
server/block_processor.py

@ -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)

16
server/cache.py

@ -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…
Cancel
Save