From a2a25e4738f0286189e51ce338b80d433a9242bb Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 21 Nov 2017 23:02:45 +0100 Subject: [PATCH 1/2] better segwit tx size estimation --- lib/coinchooser.py | 3 ++- lib/transaction.py | 36 ++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/coinchooser.py b/lib/coinchooser.py index a4d87b87..37b975c5 100644 --- a/lib/coinchooser.py +++ b/lib/coinchooser.py @@ -91,8 +91,9 @@ class CoinChooserBase(PrintError): buckets[key].append(coin) def make_Bucket(desc, coins): - size = sum(Transaction.estimated_input_size(coin) + weight = sum(Transaction.estimated_input_weight(coin) for coin in coins) + size = Transaction.virtual_size_from_weight(weight) value = sum(coin['value'] for coin in coins) return Bucket(desc, size, value, coins) diff --git a/lib/transaction.py b/lib/transaction.py index c6ec04e2..8a44e3ff 100644 --- a/lib/transaction.py +++ b/lib/transaction.py @@ -635,11 +635,11 @@ class Transaction: return pk_list, sig_list @classmethod - def serialize_witness(self, txin): + def serialize_witness(self, txin, estimate_size=False): add_w = lambda x: var_int(len(x)//2) + x if not self.is_segwit_input(txin): return '00' - pubkeys, sig_list = self.get_siglist(txin) + pubkeys, sig_list = self.get_siglist(txin, estimate_size) if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']: witness = var_int(2) + add_w(sig_list[0]) + add_w(pubkeys[0]) elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']: @@ -648,7 +648,10 @@ class Transaction: witness = var_int(n) + '00' + ''.join(add_w(x) for x in sig_list) + add_w(witness_script) else: raise BaseException('wrong txin type') - value_field = '' if self.is_txin_complete(txin) else var_int(0xffffffff) + int_to_hex(txin['value'], 8) + if self.is_txin_complete(txin) or estimate_size: + value_field = '' + else: + value_field = var_int(0xffffffff) + int_to_hex(txin['value'], 8) return value_field + witness @classmethod @@ -781,7 +784,7 @@ class Transaction: if witness and self.is_segwit(): marker = '00' flag = '01' - witness = ''.join(self.serialize_witness(x) for x in inputs) + witness = ''.join(self.serialize_witness(x, estimate_size) for x in inputs) return nVersion + marker + flag + txins + txouts + witness + nLocktime else: return nVersion + txins + txouts + nLocktime @@ -830,13 +833,26 @@ class Transaction: weights, but for simplicity we approximate that with (virtual_size)x4 """ weight = self.estimated_weight() - return weight // 4 + (weight % 4 > 0) + return self.virtual_size_from_weight(weight) @classmethod - def estimated_input_size(self, txin): - '''Return an estimated of serialized input size in bytes.''' - script = self.input_script(txin, True) - return len(self.serialize_input(txin, script)) // 2 + def estimated_input_weight(cls, txin): + '''Return an estimate of serialized input weight in weight units.''' + script = cls.input_script(txin, True) + input_size = len(cls.serialize_input(txin, script)) // 2 + + # note: we should actually branch based on tx.is_segwit() + # only if none of the inputs have a witness, is the size actually 0 + if cls.is_segwit_input(txin): + witness_size = len(cls.serialize_witness(txin, True)) // 2 + else: + witness_size = 0 + + return 4 * input_size + witness_size + + @classmethod + def virtual_size_from_weight(cls, weight): + return weight // 4 + (weight % 4 > 0) def estimated_total_size(self): """Return an estimated total transaction size in bytes.""" @@ -847,7 +863,7 @@ class Transaction: if not self.is_segwit(): return 0 inputs = self.inputs() - witness = ''.join(self.serialize_witness(x) for x in inputs) + witness = ''.join(self.serialize_witness(x, True) for x in inputs) witness_size = len(witness) // 2 + 2 # include marker and flag return witness_size From e373f505966c01d02d595963d4dead847745633e Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 22 Nov 2017 01:15:01 +0100 Subject: [PATCH 2/2] follow-up prev commit --- lib/transaction.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/transaction.py b/lib/transaction.py index 8a44e3ff..f4f359b6 100644 --- a/lib/transaction.py +++ b/lib/transaction.py @@ -863,7 +863,8 @@ class Transaction: if not self.is_segwit(): return 0 inputs = self.inputs() - witness = ''.join(self.serialize_witness(x, True) for x in inputs) + estimate = not self.is_complete() + witness = ''.join(self.serialize_witness(x, estimate) for x in inputs) witness_size = len(witness) // 2 + 2 # include marker and flag return witness_size