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. # and warranty status of this software.
from collections import namedtuple from collections import namedtuple
import binascii
import struct import struct
from lib.util import cachedproperty 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")): class Tx(namedtuple("Tx", "version inputs outputs locktime")):
@ -15,17 +14,17 @@ class Tx(namedtuple("Tx", "version inputs outputs locktime")):
def is_coinbase(self): def is_coinbase(self):
return self.inputs[0].is_coinbase 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", "prev_hash prev_idx script sequence")):
class TxInput(namedtuple("TxInput", "prevout script sequence")):
ZERO = bytes(32) ZERO = bytes(32)
MINUS_1 = 4294967295 MINUS_1 = 4294967295
@cachedproperty @cachedproperty
def is_coinbase(self): 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 @cachedproperty
def script_sig_info(self): def script_sig_info(self):
@ -34,11 +33,11 @@ class TxInput(namedtuple("TxInput", "prevout script sequence")):
return None return None
return Script.parse_script_sig(self.script) return Script.parse_script_sig(self.script)
def __repr__(self): def __str__(self):
script = binascii.hexlify(self.script).decode("ascii") script = self.script.hex()
prev_hash = binascii.hexlify(self.prevout.hash).decode("ascii") prev_hash = hash_to_str(self.prev_hash)
return ("Input(prevout=({}, {:d}), script={}, sequence={:d})" return ("Input({}, {:d}, script={}, sequence={:d})"
.format(prev_hash, self.prevout.n, script, self.sequence)) .format(prev_hash, self.prev_idx, script, self.sequence))
class TxOutput(namedtuple("TxOutput", "value pk_script")): class TxOutput(namedtuple("TxOutput", "value pk_script")):
@ -56,11 +55,12 @@ class Deserializer(object):
self.cursor = 0 self.cursor = 0
def read_tx(self): def read_tx(self):
version = self.read_le_int32() return Tx(
inputs = self.read_inputs() self.read_le_int32(), # version
outputs = self.read_outputs() self.read_inputs(), # inputs
locktime = self.read_le_uint32() self.read_outputs(), # outputs
return Tx(version, inputs, outputs, locktime) self.read_le_uint32() # locktime
)
def read_block(self): def read_block(self):
tx_hashes = [] tx_hashes = []
@ -81,15 +81,12 @@ class Deserializer(object):
return [self.read_input() for i in range(n)] return [self.read_input() for i in range(n)]
def read_input(self): def read_input(self):
prevout = self.read_outpoint() return TxInput(
script = self.read_varbytes() self.read_nbytes(32), # prev_hash
sequence = self.read_le_uint32() self.read_le_uint32(), # prev_idx
return TxInput(prevout, script, sequence) self.read_varbytes(), # script
self.read_le_uint32() # sequence
def read_outpoint(self): )
hash = self.read_nbytes(32)
n = self.read_le_uint32()
return OutPoint(hash, n)
def read_outputs(self): def read_outputs(self):
n = self.read_varint() 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) hash168s = cache.add_many(tx_hash, tx_num, tx.outputs)
if not tx.is_coinbase: if not tx.is_coinbase:
for txin in tx.inputs: for txin in tx.inputs:
hash168s.add(cache.spend(txin.prevout)) hash168s.add(cache.spend(txin))
for hash168 in hash168s: for hash168 in hash168s:
self.history[hash168].append(tx_num) self.history[hash168].append(tx_num)

16
server/cache.py

@ -120,21 +120,21 @@ class UTXOCache(LoggedClass):
return hash168s return hash168s
def spend(self, prevout): def spend(self, txin):
'''Spend a UTXO and return the address spent. '''Spend a UTXO and return the address spent.
If the UTXO is not in the cache it must be on disk. If the UTXO is not in the cache it must be on disk.
''' '''
# Fast track is it's in the cache # Fast track is it's in the cache
pack = struct.pack 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) value = self.cache.pop(key, None)
if value: if value:
self.cache_hits += 1 self.cache_hits += 1
return value[:21] return value[:21]
# Oh well. Find and remove it from the DB. # 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: if not hash168:
return None return None
@ -142,14 +142,14 @@ class UTXOCache(LoggedClass):
# Read the UTXO through the cache from the disk. We have to # Read the UTXO through the cache from the disk. We have to
# go through the cache because compressed keys can collide. # go through the cache because compressed keys can collide.
key = (b'u' + hash168 + prevout.hash[:UTXO_TX_HASH_LEN] key = (b'u' + hash168 + txin.prev_hash[:UTXO_TX_HASH_LEN]
+ pack('<H', prevout.n)) + pack('<H', txin.prev_idx))
data = self.cache_get(key) data = self.cache_get(key)
if data is None: if data is None:
# Uh-oh, this should not happen... # Uh-oh, this should not happen...
self.logger.error('found no UTXO for {} / {:d} key {}' self.logger.error('found no UTXO for {} / {:d} key {}'
.format(hash_to_str(prevout.hash), .format(hash_to_str(txin.prev_hash),
prevout.n, bytes(key).hex())) txin.prev_idx, bytes(key).hex()))
return hash168 return hash168
if len(data) == 12: if len(data) == 12:
@ -163,7 +163,7 @@ class UTXOCache(LoggedClass):
for n in range(0, len(data), 12): for n in range(0, len(data), 12):
(tx_num, ) = struct.unpack('<I', data[n:n+4]) (tx_num, ) = struct.unpack('<I', data[n:n+4])
tx_hash, height = self.parent.get_tx_hash(tx_num) 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:] data = data[:n] + data[n + 12:]
self.cache_write(key, data) self.cache_write(key, data)
return hash168 return hash168

Loading…
Cancel
Save