Browse Source

Change read_tx interface of deserializer

Most callers didn't want the hash, so create a separate call
that does both.  Add a new call that returns the TX and its vsize.
patch-2
Neil Booth 7 years ago
parent
commit
e5b4f5f316
  1. 75
      lib/tx.py
  2. 2
      server/controller.py
  3. 6
      server/mempool.py

75
lib/tx.py

@ -71,7 +71,8 @@ class TxOutput(namedtuple("TxOutput", "value pk_script")):
class Deserializer(object): class Deserializer(object):
'''Deserializes blocks into transactions. '''Deserializes blocks into transactions.
External entry points are read_tx() and read_block(). External entry points are read_tx(), read_tx_and_hash(),
read_tx_and_vsize() and read_block().
This code is performance sensitive as it is executed 100s of This code is performance sensitive as it is executed 100s of
millions of times during sync. millions of times during sync.
@ -84,25 +85,32 @@ class Deserializer(object):
self.cursor = start self.cursor = start
def read_tx(self): def read_tx(self):
'''Return a (Deserialized TX, TX_HASH) pair. '''Return a deserialized transaction.'''
The hash needs to be reversed for human display; for efficiency
we process it in the natural serialized order.
'''
start = self.cursor
return Tx( return Tx(
self._read_le_int32(), # version self._read_le_int32(), # version
self._read_inputs(), # inputs self._read_inputs(), # inputs
self._read_outputs(), # outputs self._read_outputs(), # outputs
self._read_le_uint32() # locktime self._read_le_uint32() # locktime
), double_sha256(self.binary[start:self.cursor]) )
def read_tx_and_hash(self):
'''Return a (deserialized TX, tx_hash) pair.
The hash needs to be reversed for human display; for efficiency
we process it in the natural serialized order.
'''
start = self.cursor
return self.read_tx(), double_sha256(self.binary[start:self.cursor])
def read_tx_and_vsize(self):
'''Return a (deserialized TX, vsize) pair.'''
return self.read_tx(), self.binary_length
def read_tx_block(self): def read_tx_block(self):
'''Returns a list of (deserialized_tx, tx_hash) pairs.''' '''Returns a list of (deserialized_tx, tx_hash) pairs.'''
read_tx = self.read_tx read = self.read_tx_and_hash
txs = [read_tx() for _ in range(self._read_varint())]
# Some coins have excess data beyond the end of the transactions # Some coins have excess data beyond the end of the transactions
return txs return [read() for _ in range(self._read_varint())]
def _read_inputs(self): def _read_inputs(self):
read_input = self._read_input read_input = self._read_input
@ -198,15 +206,12 @@ class DeserializerSegWit(Deserializer):
read_varbytes = self._read_varbytes read_varbytes = self._read_varbytes
return [read_varbytes() for i in range(self._read_varint())] return [read_varbytes() for i in range(self._read_varint())]
def read_tx(self): def _read_tx_parts(self):
'''Return a (Deserialized TX, TX_HASH) pair. '''Return a (deserialized TX, tx_hash, vsize) tuple.'''
The hash needs to be reversed for human display; for efficiency
we process it in the natural serialized order.
'''
marker = self.binary[self.cursor + 4] marker = self.binary[self.cursor + 4]
if marker: if marker:
return super().read_tx() tx, tx_hash = super().read_tx_and_hash()
return tx, tx_hash, self.binary_size
# Ugh, this is nasty. # Ugh, this is nasty.
start = self.cursor start = self.cursor
@ -221,14 +226,27 @@ class DeserializerSegWit(Deserializer):
outputs = self._read_outputs() outputs = self._read_outputs()
orig_ser += self.binary[start:self.cursor] orig_ser += self.binary[start:self.cursor]
base_size = self.cursor - start
witness = self._read_witness(len(inputs)) witness = self._read_witness(len(inputs))
start = self.cursor start = self.cursor
locktime = self._read_le_uint32() locktime = self._read_le_uint32()
orig_ser += self.binary[start:self.cursor] orig_ser += self.binary[start:self.cursor]
vsize = (3 * base_size + self.binary_length) // 4
return TxSegWit(version, marker, flag, inputs, outputs, witness,
locktime), double_sha256(orig_ser), vsize
def read_tx(self):
return self._read_tx_parts()[0]
def read_tx_and_hash(self):
tx, tx_hash, vsize = self._read_tx_parts()
return tx, tx_hash
return TxSegWit(version, marker, flag, inputs, def read_tx_and_vsize(self):
outputs, witness, locktime), double_sha256(orig_ser) tx, tx_hash, vsize = self._read_tx_parts()
return tx, vsize
class DeserializerAuxPow(Deserializer): class DeserializerAuxPow(Deserializer):
@ -289,7 +307,6 @@ class TxJoinSplit(namedtuple("Tx", "version inputs outputs locktime")):
class DeserializerZcash(DeserializerEquihash): class DeserializerZcash(DeserializerEquihash):
def read_tx(self): def read_tx(self):
start = self.cursor
base_tx = TxJoinSplit( base_tx = TxJoinSplit(
self._read_le_int32(), # version self._read_le_int32(), # version
self._read_inputs(), # inputs self._read_inputs(), # inputs
@ -302,7 +319,7 @@ class DeserializerZcash(DeserializerEquihash):
self.cursor += joinsplit_size * 1802 # JSDescription self.cursor += joinsplit_size * 1802 # JSDescription
self.cursor += 32 # joinSplitPubKey self.cursor += 32 # joinSplitPubKey
self.cursor += 64 # joinSplitSig self.cursor += 64 # joinSplitSig
return base_tx, double_sha256(self.binary[start:self.cursor]) return base_tx
class TxTime(namedtuple("Tx", "version time inputs outputs locktime")): class TxTime(namedtuple("Tx", "version time inputs outputs locktime")):
@ -315,21 +332,17 @@ class TxTime(namedtuple("Tx", "version time inputs outputs locktime")):
class DeserializerTxTime(Deserializer): class DeserializerTxTime(Deserializer):
def read_tx(self): def read_tx(self):
start = self.cursor
return TxTime( return TxTime(
self._read_le_int32(), # version self._read_le_int32(), # version
self._read_le_uint32(), # time self._read_le_uint32(), # time
self._read_inputs(), # inputs self._read_inputs(), # inputs
self._read_outputs(), # outputs self._read_outputs(), # outputs
self._read_le_uint32(), # locktime self._read_le_uint32(), # locktime
), double_sha256(self.binary[start:self.cursor]) )
class DeserializerReddcoin(Deserializer): class DeserializerReddcoin(Deserializer):
def read_tx(self): def read_tx(self):
start = self.cursor
version = self._read_le_int32() version = self._read_le_int32()
inputs = self._read_inputs() inputs = self._read_inputs()
outputs = self._read_outputs() outputs = self._read_outputs()
@ -339,13 +352,7 @@ class DeserializerReddcoin(Deserializer):
else: else:
time = 0 time = 0
return TxTime( return TxTime(version, time, inputs, outputs, locktime)
version,
time,
inputs,
outputs,
locktime,
), double_sha256(self.binary[start:self.cursor])
class DeserializerTxTimeAuxPow(DeserializerTxTime): class DeserializerTxTimeAuxPow(DeserializerTxTime):

2
server/controller.py

@ -873,7 +873,7 @@ class Controller(ServerBase):
if not raw_tx: if not raw_tx:
return None return None
raw_tx = util.hex_to_bytes(raw_tx) raw_tx = util.hex_to_bytes(raw_tx)
tx, tx_hash = self.coin.DESERIALIZER(raw_tx).read_tx() tx = self.coin.DESERIALIZER(raw_tx).read_tx()
if index >= len(tx.outputs): if index >= len(tx.outputs):
return None return None
return self.coin.address_from_script(tx.outputs[index].pk_script) return self.coin.address_from_script(tx.outputs[index].pk_script)

6
server/mempool.py

@ -217,7 +217,7 @@ class MemPool(util.LoggedClass):
for tx_hash, raw_tx in raw_tx_map.items(): for tx_hash, raw_tx in raw_tx_map.items():
if tx_hash not in txs: if tx_hash not in txs:
continue continue
tx, _tx_hash = deserializer(raw_tx).read_tx() tx = deserializer(raw_tx).read_tx()
# Convert the tx outputs into (hashX, value) pairs # Convert the tx outputs into (hashX, value) pairs
txout_pairs = [(script_hashX(txout.pk_script), txout.value) txout_pairs = [(script_hashX(txout.pk_script), txout.value)
@ -301,7 +301,7 @@ class MemPool(util.LoggedClass):
txin_pairs, txout_pairs = item txin_pairs, txout_pairs = item
tx_fee = (sum(v for hashX, v in txin_pairs) - tx_fee = (sum(v for hashX, v in txin_pairs) -
sum(v for hashX, v in txout_pairs)) sum(v for hashX, v in txout_pairs))
tx, tx_hash = deserializer(raw_tx).read_tx() tx = deserializer(raw_tx).read_tx()
unconfirmed = any(hash_to_str(txin.prev_hash) in self.txs unconfirmed = any(hash_to_str(txin.prev_hash) in self.txs
for txin in tx.inputs) for txin in tx.inputs)
result.append((hex_hash, tx_fee, unconfirmed)) result.append((hex_hash, tx_fee, unconfirmed))
@ -319,7 +319,7 @@ class MemPool(util.LoggedClass):
for hex_hash, raw_tx in pairs: for hex_hash, raw_tx in pairs:
if not raw_tx: if not raw_tx:
continue continue
tx, tx_hash = deserializer(raw_tx).read_tx() tx = deserializer(raw_tx).read_tx()
for txin in tx.inputs: for txin in tx.inputs:
spends.add((txin.prev_hash, txin.prev_idx)) spends.add((txin.prev_hash, txin.prev_idx))
return spends return spends

Loading…
Cancel
Save