You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.8 KiB
117 lines
3.8 KiB
# It assumes largest possible signature size for all inputs. For segwit
|
|
# multisig it can be .25 bytes off due to difference between segwit
|
|
# encoding (varint) vs. non-segwit encoding (op_push) of the multisig script.
|
|
#
|
|
# Heavily inspired by:
|
|
# https://github.com/trezor/trezor-mcu/commit/e1fa7af1da79e86ccaae5f3cd2a6c4644f546f8a
|
|
|
|
from micropython import const
|
|
|
|
from trezor.messages import InputScriptType
|
|
|
|
if False:
|
|
from trezor.messages.TxInput import TxInput
|
|
|
|
# transaction header size: 4 byte version
|
|
_TXSIZE_HEADER = const(4)
|
|
# transaction footer size: 4 byte lock time
|
|
_TXSIZE_FOOTER = const(4)
|
|
# transaction segwit overhead 2 (marker, flag)
|
|
_TXSIZE_SEGWIT_OVERHEAD = const(2)
|
|
|
|
# transaction input size (without script): 32 prevhash, 4 idx, 4 sequence
|
|
_TXSIZE_INPUT = const(40)
|
|
# transaction output size (without script): 8 amount
|
|
_TXSIZE_OUTPUT = const(8)
|
|
# size of a pubkey
|
|
_TXSIZE_PUBKEY = const(33)
|
|
# size of a DER signature (3 type bytes, 3 len bytes, 33 R, 32 S, 1 sighash
|
|
_TXSIZE_SIGNATURE = const(72)
|
|
# size of a multiscript without pubkey (1 M, 1 N, 1 checksig)
|
|
_TXSIZE_MULTISIGSCRIPT = const(3)
|
|
# size of a p2wpkh script (1 version, 1 push, 20 hash)
|
|
_TXSIZE_WITNESSPKHASH = const(22)
|
|
# size of a p2wsh script (1 version, 1 push, 32 hash)
|
|
_TXSIZE_WITNESSSCRIPT = const(34)
|
|
|
|
|
|
class TxWeightCalculator:
|
|
def __init__(self, inputs_count: int, outputs_count: int):
|
|
self.inputs_count = inputs_count
|
|
self.counter = 4 * (
|
|
_TXSIZE_HEADER
|
|
+ _TXSIZE_FOOTER
|
|
+ self.ser_length_size(inputs_count)
|
|
+ self.ser_length_size(outputs_count)
|
|
)
|
|
self.segwit = False
|
|
|
|
def add_witness_header(self) -> None:
|
|
if not self.segwit:
|
|
self.counter += _TXSIZE_SEGWIT_OVERHEAD
|
|
self.counter += self.ser_length_size(self.inputs_count)
|
|
self.segwit = True
|
|
|
|
def add_input(self, i: TxInput) -> None:
|
|
|
|
if i.multisig:
|
|
multisig_script_size = _TXSIZE_MULTISIGSCRIPT + len(i.multisig.pubkeys) * (
|
|
1 + _TXSIZE_PUBKEY
|
|
)
|
|
input_script_size = (
|
|
1
|
|
+ i.multisig.m * (1 + _TXSIZE_SIGNATURE) # the OP_FALSE bug in multisig
|
|
+ self.op_push_size(multisig_script_size)
|
|
+ multisig_script_size
|
|
)
|
|
else:
|
|
input_script_size = 1 + _TXSIZE_SIGNATURE + 1 + _TXSIZE_PUBKEY
|
|
|
|
self.counter += 4 * _TXSIZE_INPUT
|
|
|
|
if (
|
|
i.script_type == InputScriptType.SPENDADDRESS
|
|
or i.script_type == InputScriptType.SPENDMULTISIG
|
|
):
|
|
input_script_size += self.ser_length_size(input_script_size)
|
|
self.counter += 4 * input_script_size
|
|
|
|
elif (
|
|
i.script_type == InputScriptType.SPENDWITNESS
|
|
or i.script_type == InputScriptType.SPENDP2SHWITNESS
|
|
):
|
|
self.add_witness_header()
|
|
if i.script_type == InputScriptType.SPENDP2SHWITNESS:
|
|
if i.multisig:
|
|
self.counter += 4 * (2 + _TXSIZE_WITNESSSCRIPT)
|
|
else:
|
|
self.counter += 4 * (2 + _TXSIZE_WITNESSPKHASH)
|
|
else:
|
|
self.counter += 4 # empty
|
|
self.counter += input_script_size # discounted witness
|
|
|
|
def add_output(self, script: bytes) -> None:
|
|
size = len(script) + self.ser_length_size(len(script))
|
|
self.counter += 4 * (_TXSIZE_OUTPUT + size)
|
|
|
|
def get_total(self) -> int:
|
|
return self.counter
|
|
|
|
@staticmethod
|
|
def ser_length_size(length: int) -> int:
|
|
if length < 253:
|
|
return 1
|
|
if length < 0x10000:
|
|
return 3
|
|
return 5
|
|
|
|
@staticmethod
|
|
def op_push_size(length: int) -> int:
|
|
if length < 0x4C:
|
|
return 1
|
|
if length < 0x100:
|
|
return 2
|
|
if length < 0x10000:
|
|
return 3
|
|
return 5
|