feat(core): Support Taproot in TxWeightCalculator.

pull/1918/head
Andrew Kozlik 3 years ago committed by Andrew Kozlik
parent a635876807
commit f9f0b517e2

@ -7,6 +7,7 @@
from micropython import const
from trezor import wire
from trezor.enums import InputScriptType
from .. import common
@ -28,7 +29,9 @@ _TXSIZE_OUTPUT = const(8)
# size of a pubkey
_TXSIZE_PUBKEY = const(33)
# maximum size of a DER signature (3 type bytes, 3 len bytes, 33 R, 32 S, 1 sighash)
_TXSIZE_SIGNATURE = const(72)
_TXSIZE_DER_SIGNATURE = const(72)
# size of a Schnorr signature (32 R, 32 S, no sighash)
_TXSIZE_SCHNORR_SIGNATURE = const(64)
# 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)
@ -48,6 +51,9 @@ class TxWeightCalculator:
self.inputs_count += 1
if i.multisig:
if i.script_type == InputScriptType.SPENDTAPROOT:
raise wire.ProcessError("Multisig not supported for taproot")
n = len(i.multisig.nodes) if i.multisig.nodes else len(i.multisig.pubkeys)
multisig_script_size = _TXSIZE_MULTISIGSCRIPT + n * (1 + _TXSIZE_PUBKEY)
if i.script_type in common.SEGWIT_INPUT_SCRIPT_TYPES:
@ -57,11 +63,13 @@ class TxWeightCalculator:
input_script_size = (
1 # the OP_FALSE bug in multisig
+ i.multisig.m * (1 + _TXSIZE_SIGNATURE)
+ i.multisig.m * (1 + _TXSIZE_DER_SIGNATURE)
+ multisig_script_size
)
elif i.script_type == InputScriptType.SPENDTAPROOT:
input_script_size = 1 + _TXSIZE_SCHNORR_SIGNATURE
else:
input_script_size = 1 + _TXSIZE_SIGNATURE + 1 + _TXSIZE_PUBKEY
input_script_size = 1 + _TXSIZE_DER_SIGNATURE + 1 + _TXSIZE_PUBKEY
self.counter += 4 * _TXSIZE_INPUT

@ -11,7 +11,7 @@ from apps.common import coins
from apps.common.keychain import Keychain
from apps.common.paths import AlwaysMatchingSchema
from apps.bitcoin.sign_tx.tx_weight import *
from apps.bitcoin.scripts import output_derive_script
from apps.bitcoin.scripts import output_derive_script, output_script_paytoopreturn
class TestCalculateTxWeight(unittest.TestCase):
@ -122,6 +122,30 @@ class TestCalculateTxWeight(unittest.TestCase):
# segwit: segwit header, witness stack item count, witness 1*(2+1+107) = 110
# total 566
def test_taproot_txweight(self):
coin = coins.by_name('Testnet')
inp1 = TxInput(
address_n=[86 | 0x80000000, 1 | 0x80000000, 0 | 0x80000000, 1, 0],
amount=4600,
prev_hash=unhexlify('7956f1de3e7362b04115b64a31f0b6822c50dd6c08d78398f392a0ac3f0e357b'),
prev_index=1,
script_type=InputScriptType.SPENDTAPROOT,
)
out1 = TxOutput(
address="tb1paxhjl357yzctuf3fe58fcdx6nul026hhh6kyldpfsf3tckj9a3wslqd7zd",
amount=4450,
script_type=OutputScriptType.PAYTOADDRESS,
)
calculator = TxWeightCalculator()
calculator.add_input(inp1)
calculator.add_output(output_derive_script(out1.address, coin))
# 010000000001017b350e3faca092f39883d7086cdd502c82b6f0314ab61541b062733edef156790100000000ffffffff016211000000000000225120e9af2fc69e20b0be2629cd0e9c34da9f3ef56af7beac4fb4298262bc5a45ec5d0140493145b992dacbd7ea579a415efc2cba20c3bf0f7827d1bcf999109c0d11783fe96f91ddb04a889faa17ad21ecc5c81a578009744e95c7e721aff2a5c442916600000000
self.assertEqual(calculator.get_total(), 4*94 + 68)
def test_legacy_multisig_txweight(self):
coin = coins.by_name('Bitcoin')
seed = bip39.seed(' '.join(['all'] * 12), '')
@ -206,6 +230,79 @@ class TestCalculateTxWeight(unittest.TestCase):
# 01000000000101be0210025c5be68a473f6a38bf53b53bc88d5c46567616026dc056e72b92319c01000000232200208d398cfb58a1d9cdb59ccbce81559c095e8c6f4a3e64966ca385078d9879f95effffffff01887d180000000000220020c5f4a0a4ea7c0392efe0a9670a73264cffa90b19107cd8a8e9750ff93c77fdfb0400483045022100dd6342c65197af27d7894d8b8b88b16b568ee3b5ebfdc55fdfb7caa9650e3b4c02200c7074a5bcb0068f63d9014c7cd2b0490aba75822d315d41aad444e9b86adf5201483045022100e7e6c2d21109512ba0609e93903e84bfb7731ac3962ee2c1cad54a7a30ff99a20220421497930226c39fc3834e8d6da3fc876516239518b0e82e2dc1e3c46271a17c01695221021630971f20fa349ba940a6ba3706884c41579cd760c89901374358db5dd545b92102f2ff4b353702d2bb03d4c494be19d77d0ab53d16161b53fbcaf1afeef4ad0cb52103e9b6b1c691a12ce448f1aedbbd588e064869c79fbd760eae3b8cd8a5f1a224db53ae00000000
self.assertEqual(calculator.get_total(), 4*129 + 256)
def test_mixed_txweight(self):
coin = coins.by_name('Testnet')
inp1 = TxInput(
address_n=[49 | 0x80000000, 1 | 0x80000000, 1 | 0x80000000, 0, 0],
amount=20000,
prev_hash=unhexlify('8c3ea7a10ab6d289119b722ec8c27b70c17c722334ced31a0370d782e4b6775d'),
prev_index=0,
script_type=InputScriptType.SPENDP2SHWITNESS,
)
inp2 = TxInput(
address_n=[84 | 0x80000000, 1 | 0x80000000, 1 | 0x80000000, 0, 0],
amount=15000,
prev_hash=unhexlify('7956f1de3e7362b04115b64a31f0b6822c50dd6c08d78398f392a0ac3f0e357b'),
prev_index=0,
script_type=InputScriptType.SPENDWITNESS,
)
inp3 = TxInput(
address_n=[86 | 0x80000000, 1 | 0x80000000, 1 | 0x80000000, 0, 0],
amount=4450,
prev_hash=unhexlify('7956f1de3e7362b04115b64a31f0b6822c50dd6c08d78398f392a0ac3f0e357b'),
prev_index=0,
script_type=InputScriptType.SPENDTAPROOT,
)
inp4 = TxInput(
address_n=[44 | 0x80000000, 1 | 0x80000000, 1 | 0x80000000, 0, 0],
amount=10000,
prev_hash=unhexlify('3ac32e90831d79385eee49d6030a2123cd9d009fe8ffc3d470af9a6a777a119b'),
prev_index=2,
script_type=InputScriptType.SPENDADDRESS,
)
out1 = TxOutput(
address="tb1q6xnnna3g7lk22h5tn8nlx2ezmndlvuk556w4w3",
amount=25000,
script_type=OutputScriptType.PAYTOWITNESS,
)
out2 = TxOutput(
address="mfnMbVFC1rH4p9GNbjkMfrAjyKRLycFAzA",
script_type=OutputScriptType.PAYTOADDRESS,
amount=7000,
)
out3 = TxOutput(
address="2MvAG8m2xSf83FgeR4ZpUtaubpLNjAMMoka",
amount=6900,
script_type=OutputScriptType.PAYTOP2SHWITNESS,
)
out4 = TxOutput(
op_return_data=b"test of op_return data",
amount=0,
script_type=OutputScriptType.PAYTOOPRETURN,
)
out5 = TxOutput(
address="tb1ptgp9w0mm89ms43flw0gkrhyx75gyc6qjhtpf0jmt5sv0dufpnsrsyv9nsz",
amount=10000,
script_type=OutputScriptType.PAYTOTAPROOT,
)
calculator = TxWeightCalculator()
calculator.add_input(inp1)
calculator.add_input(inp2)
calculator.add_input(inp3)
calculator.add_input(inp4)
calculator.add_output(output_derive_script(out1.address, coin))
calculator.add_output(output_derive_script(out2.address, coin))
calculator.add_output(output_derive_script(out3.address, coin))
calculator.add_output(output_script_paytoopreturn(out4.op_return_data))
calculator.add_output(output_derive_script(out5.address, coin))
# 010000000001045d77b6e482d770031ad3ce3423727cc1707bc2c82e729b1189d2b60aa1a73e8c0000000017160014a33c6e24c99e108b97bc411e7e9ef31e9d5d6164ffffffff7b350e3faca092f39883d7086cdd502c82b6f0314ab61541b062733edef156790000000000ffffffff852e125137abca2dd7a42837dccfc34edc358c72eefd62978d6747d3be9315900000000000ffffffff9b117a776a9aaf70d4c3ffe89f009dcd23210a03d649ee5e38791d83902ec33a020000006b483045022100f6bd64136839b49822cf7e2050bc5c91346fc18b5cf97a945d4fd6c502f712d002207d1859e66d218f705b704f3cfca0c75410349bb1f50623f4fc2d09d5d8df0a3f012103bae960983f83e28fcb8f0e5f3dc1f1297b9f9636612fd0835b768e1b7275fb9dffffffff05a861000000000000160014d1a739f628f7eca55e8b99e7f32b22dcdbf672d4581b0000000000001976a91402e9b094fd98e2a26e805894eb78f7ff3fef199b88acf41a00000000000017a9141ff816cbeb74817050de585ceb2c772ebf71147a870000000000000000186a1674657374206f66206f705f72657475726e206461746110270000000000002251205a02573f7b39770ac53f73d161dc86f5104c6812bac297cb6ba418f6f1219c070247304402205fae7fa2b5141548593d5623ce5bd82ee18dfc751c243526039c91848efd603702200febfbe3467a68c599245ff89055514f26e146c79b58d932ced2325e6dad1b1a0121021630971f20fa349ba940a6ba3706884c41579cd760c89901374358db5dd545b90247304402201b21212100c84207697cebb852374669c382ed97cbd08afbbdfe1b302802161602206b32b2140d094cf5b7e758135961c95478c8e82fea0df30f56ccee284b79eaea012103f6b2377d52960a6094ec158cf19dcf9e33b3da4798c2302aa5806483ed4187ae01404a81e4b7f55d6d4a26923c5e2daf3cc86ed6030f83ea6e7bb16d7b81b988b34585be21a64ab45ddcc2fb9f17be2dfeff6b22cf943bc3fc8f125a7f463af428ed0000000000
# The witness data is 283 bytes, but two of the DER signatures are one byte below the
# average length, so the caculator should estimate 285 bytes of witness data.
self.assertEqual(calculator.get_total(), 4*477 + 285)
if __name__ == '__main__':
unittest.main()

Loading…
Cancel
Save