From a6d46fab8ddef201a299cf83362047e5bc61e86b Mon Sep 17 00:00:00 2001 From: "John L. Jegutanis" Date: Thu, 16 Aug 2018 14:37:50 +0200 Subject: [PATCH] Improve generation inputs handling Fixes #570 --- electrumx/lib/tx.py | 54 ++++++----------------------- electrumx/server/block_processor.py | 27 ++++++++------- electrumx/server/mempool.py | 16 ++++++--- tests/server/test_mempool.py | 15 +++++++- 4 files changed, 52 insertions(+), 60 deletions(-) diff --git a/electrumx/lib/tx.py b/electrumx/lib/tx.py index 8dc5d55..3f26cbb 100644 --- a/electrumx/lib/tx.py +++ b/electrumx/lib/tx.py @@ -31,20 +31,23 @@ from collections import namedtuple from electrumx.lib.hash import sha256, double_sha256, hash_to_hex_str from electrumx.lib.util import ( - cachedproperty, unpack_le_int32_from, unpack_le_int64_from, - unpack_le_uint16_from, unpack_le_uint32_from, unpack_le_uint64_from, - pack_le_int32, pack_varint, pack_le_uint32, pack_le_uint32, pack_le_int64, - pack_varbytes, + unpack_le_int32_from, unpack_le_int64_from, unpack_le_uint16_from, + unpack_le_uint32_from, unpack_le_uint64_from, pack_le_int32, pack_varint, + pack_le_uint32, pack_le_int64, pack_varbytes, ) +ZERO = bytes(32) +MINUS_1 = 4294967295 + + +def is_gen_outpoint(hash, index): + '''Test if an outpoint is a generation/coinbase like''' + return index == MINUS_1 and hash == ZERO + class Tx(namedtuple("Tx", "version inputs outputs locktime")): '''Class representing a transaction.''' - @cachedproperty - def is_generation(self): - return self.inputs[0].is_generation - def serialize(self): return b''.join(( pack_le_int32(self.version), @@ -58,15 +61,6 @@ class Tx(namedtuple("Tx", "version inputs outputs locktime")): class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")): '''Class representing a transaction input.''' - - ZERO = bytes(32) - MINUS_1 = 4294967295 - - @cachedproperty - def is_generation(self): - return (self.prev_idx == TxInput.MINUS_1 and - self.prev_hash == TxInput.ZERO) - def __str__(self): script = self.script.hex() prev_hash = hash_to_hex_str(self.prev_hash) @@ -214,10 +208,6 @@ class TxSegWit(namedtuple("Tx", "version marker flag inputs outputs " "witness locktime")): '''Class representing a SegWit transaction.''' - @cachedproperty - def is_generation(self): - return self.inputs[0].is_generation - class DeserializerSegWit(Deserializer): @@ -326,10 +316,6 @@ class DeserializerEquihashSegWit(DeserializerSegWit, DeserializerEquihash): class TxJoinSplit(namedtuple("Tx", "version inputs outputs locktime")): '''Class representing a JoinSplit transaction.''' - @cachedproperty - def is_generation(self): - return self.inputs[0].is_generation if len(self.inputs) > 0 else False - class DeserializerZcash(DeserializerEquihash): def read_tx(self): @@ -360,10 +346,6 @@ class DeserializerZcash(DeserializerEquihash): class TxTime(namedtuple("Tx", "version time inputs outputs locktime")): '''Class representing transaction that has a time field.''' - @cachedproperty - def is_generation(self): - return self.inputs[0].is_generation - class DeserializerTxTime(Deserializer): def read_tx(self): @@ -445,11 +427,6 @@ class DeserializerGroestlcoin(DeserializerSegWit): class TxInputDcr(namedtuple("TxInput", "prev_hash prev_idx tree sequence")): '''Class representing a Decred transaction input.''' - @cachedproperty - def is_generation(self): - return (self.prev_idx == TxInput.MINUS_1 and - self.prev_hash == TxInput.ZERO) - def __str__(self): prev_hash = hash_to_hex_str(self.prev_hash) return ("Input({}, {:d}, tree={}, sequence={:d})" @@ -465,10 +442,6 @@ class TxDcr(namedtuple("Tx", "version inputs outputs locktime expiry " "witness")): '''Class representing a Decred transaction.''' - @cachedproperty - def is_generation(self): - return self.inputs[0].is_generation - class DeserializerDecred(Deserializer): @staticmethod @@ -541,11 +514,6 @@ class DeserializerDecred(Deserializer): end_prefix = self.cursor witness = self._read_witness(len(inputs)) - # Drop the coinbase-like input from a vote tx as it creates problems - # with UTXOs lookups and mempool management - if inputs[0].is_generation and len(inputs) > 1: - inputs = inputs[1:] - if produce_hash: # TxSerializeNoWitness << 16 == 0x10000 no_witness_header = pack_le_uint32(0x10000 | (version & 0xffff)) diff --git a/electrumx/server/block_processor.py b/electrumx/server/block_processor.py index e658fe5..2af4ff7 100644 --- a/electrumx/server/block_processor.py +++ b/electrumx/server/block_processor.py @@ -18,6 +18,7 @@ from functools import partial from aiorpcx import TaskGroup, run_in_thread import electrumx +from electrumx.lib.tx import is_gen_outpoint from electrumx.server.daemon import DaemonError from electrumx.lib.hash import hash_to_hex_str, HASHX_LEN from electrumx.lib.util import chunks, class_logger @@ -411,11 +412,12 @@ class BlockProcessor(object): tx_numb = s_pack('