mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
core/sign_tx: Use a preallocated buffer for transaction serialization.
This commit is contained in:
parent
555259d6a9
commit
2ceb091d68
@ -80,16 +80,11 @@ class Bitcoinlike(signing.Bitcoin):
|
|||||||
txi_sign.script_sig = self.input_derive_script(
|
txi_sign.script_sig = self.input_derive_script(
|
||||||
txi_sign, key_sign_pub, signature
|
txi_sign, key_sign_pub, signature
|
||||||
)
|
)
|
||||||
w_txi_sign = writers.empty_bytearray(
|
|
||||||
5 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4
|
|
||||||
)
|
|
||||||
if i_sign == 0: # serializing first input => prepend headers
|
if i_sign == 0: # serializing first input => prepend headers
|
||||||
self.write_sign_tx_header(w_txi_sign, True in self.segwit.values())
|
self.write_sign_tx_header(self.serialized_tx, True in self.segwit.values())
|
||||||
writers.write_tx_input(w_txi_sign, txi_sign)
|
writers.write_tx_input(self.serialized_tx, txi_sign)
|
||||||
self.tx_ser.signature_index = i_sign
|
self.tx_req.serialized.signature_index = i_sign
|
||||||
self.tx_ser.signature = signature
|
self.tx_req.serialized.signature = signature
|
||||||
self.tx_ser.serialized_tx = w_txi_sign
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
def on_negative_fee(self) -> None:
|
def on_negative_fee(self) -> None:
|
||||||
# some coins require negative fees for reward TX
|
# some coins require negative fees for reward TX
|
||||||
|
@ -78,16 +78,10 @@ class Decred(Bitcoin):
|
|||||||
await super().process_input(i, txi)
|
await super().process_input(i, txi)
|
||||||
|
|
||||||
# Decred serializes inputs early.
|
# Decred serializes inputs early.
|
||||||
w_txi = writers.empty_bytearray(8 if i == 0 else 0 + 9 + len(txi.prev_hash))
|
|
||||||
if i == 0: # serializing first input => prepend headers
|
if i == 0: # serializing first input => prepend headers
|
||||||
self.write_sign_tx_header(w_txi, False)
|
self.write_sign_tx_header(self.serialized_tx, False)
|
||||||
|
|
||||||
self.write_tx_input(w_txi, txi)
|
self.write_tx_input(self.serialized_tx, txi)
|
||||||
|
|
||||||
self.tx_ser.signature_index = None
|
|
||||||
self.tx_ser.signature = None
|
|
||||||
self.tx_ser.serialized_tx = w_txi
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def confirm_output(
|
async def confirm_output(
|
||||||
self, i: int, txo: TxOutputType, txo_bin: TxOutputBinType
|
self, i: int, txo: TxOutputType, txo_bin: TxOutputBinType
|
||||||
@ -99,17 +93,11 @@ class Decred(Bitcoin):
|
|||||||
)
|
)
|
||||||
txo_bin.decred_script_version = txo.decred_script_version
|
txo_bin.decred_script_version = txo.decred_script_version
|
||||||
|
|
||||||
w_txo_bin = writers.empty_bytearray(4 + 8 + 2 + 4 + len(txo_bin.script_pubkey))
|
|
||||||
if i == 0: # serializing first output => prepend outputs count
|
if i == 0: # serializing first output => prepend outputs count
|
||||||
writers.write_varint(w_txo_bin, self.tx.outputs_count)
|
writers.write_varint(self.serialized_tx, self.tx.outputs_count)
|
||||||
self.hash143.add_output_count(self.tx)
|
self.hash143.add_output_count(self.tx)
|
||||||
|
|
||||||
writers.write_tx_output(w_txo_bin, txo_bin)
|
writers.write_tx_output(self.serialized_tx, txo_bin)
|
||||||
|
|
||||||
self.tx_ser.signature_index = None
|
|
||||||
self.tx_ser.signature = None
|
|
||||||
self.tx_ser.serialized_tx = w_txo_bin
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
await super().confirm_output(i, txo, txo_bin)
|
await super().confirm_output(i, txo, txo_bin)
|
||||||
|
|
||||||
@ -169,21 +157,15 @@ class Decred(Bitcoin):
|
|||||||
txi_sign, key_sign_pub, signature
|
txi_sign, key_sign_pub, signature
|
||||||
)
|
)
|
||||||
|
|
||||||
max_witness_size = 8 + 4 + 4 + 4 + len(txi_sign.script_sig)
|
|
||||||
if i_sign == 0:
|
if i_sign == 0:
|
||||||
w_txi_sign = writers.empty_bytearray(4 + 4 + 4 + max_witness_size)
|
writers.write_uint32(self.serialized_tx, self.tx.lock_time)
|
||||||
writers.write_uint32(w_txi_sign, self.tx.lock_time)
|
writers.write_uint32(self.serialized_tx, self.tx.expiry)
|
||||||
writers.write_uint32(w_txi_sign, self.tx.expiry)
|
writers.write_varint(self.serialized_tx, self.tx.inputs_count)
|
||||||
writers.write_varint(w_txi_sign, self.tx.inputs_count)
|
|
||||||
else:
|
|
||||||
w_txi_sign = writers.empty_bytearray(max_witness_size)
|
|
||||||
|
|
||||||
writers.write_tx_input_decred_witness(w_txi_sign, txi_sign)
|
writers.write_tx_input_decred_witness(self.serialized_tx, txi_sign)
|
||||||
|
|
||||||
self.tx_ser.signature_index = i_sign
|
self.tx_req.serialized.signature_index = i_sign
|
||||||
self.tx_ser.signature = signature
|
self.tx_req.serialized.signature = signature
|
||||||
self.tx_ser.serialized_tx = w_txi_sign
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def step5_serialize_outputs(self) -> None:
|
async def step5_serialize_outputs(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
@ -117,7 +117,9 @@ def request_tx_meta(tx_req: TxRequest, coin: CoinInfo, tx_hash: bytes = None) ->
|
|||||||
tx_req.details.tx_hash = tx_hash
|
tx_req.details.tx_hash = tx_hash
|
||||||
tx_req.details.request_index = None
|
tx_req.details.request_index = None
|
||||||
ack = yield tx_req
|
ack = yield tx_req
|
||||||
tx_req.serialized = None
|
tx_req.serialized.signature = None
|
||||||
|
tx_req.serialized.signature_index = None
|
||||||
|
tx_req.serialized.serialized_tx[:] = bytes()
|
||||||
gc.collect()
|
gc.collect()
|
||||||
return sanitize_tx_meta(ack.tx, coin)
|
return sanitize_tx_meta(ack.tx, coin)
|
||||||
|
|
||||||
@ -131,7 +133,9 @@ def request_tx_extra_data( # type: ignore
|
|||||||
tx_req.details.tx_hash = tx_hash
|
tx_req.details.tx_hash = tx_hash
|
||||||
tx_req.details.request_index = None
|
tx_req.details.request_index = None
|
||||||
ack = yield tx_req
|
ack = yield tx_req
|
||||||
tx_req.serialized = None
|
tx_req.serialized.signature = None
|
||||||
|
tx_req.serialized.signature_index = None
|
||||||
|
tx_req.serialized.serialized_tx[:] = bytes()
|
||||||
tx_req.details.extra_data_offset = None
|
tx_req.details.extra_data_offset = None
|
||||||
tx_req.details.extra_data_len = None
|
tx_req.details.extra_data_len = None
|
||||||
gc.collect()
|
gc.collect()
|
||||||
@ -143,7 +147,9 @@ def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes =
|
|||||||
tx_req.details.request_index = i
|
tx_req.details.request_index = i
|
||||||
tx_req.details.tx_hash = tx_hash
|
tx_req.details.tx_hash = tx_hash
|
||||||
ack = yield tx_req
|
ack = yield tx_req
|
||||||
tx_req.serialized = None
|
tx_req.serialized.signature = None
|
||||||
|
tx_req.serialized.signature_index = None
|
||||||
|
tx_req.serialized.serialized_tx[:] = bytes()
|
||||||
gc.collect()
|
gc.collect()
|
||||||
return sanitize_tx_input(ack.tx, coin)
|
return sanitize_tx_input(ack.tx, coin)
|
||||||
|
|
||||||
@ -153,7 +159,9 @@ def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: bytes
|
|||||||
tx_req.details.request_index = i
|
tx_req.details.request_index = i
|
||||||
tx_req.details.tx_hash = tx_hash
|
tx_req.details.tx_hash = tx_hash
|
||||||
ack = yield tx_req
|
ack = yield tx_req
|
||||||
tx_req.serialized = None
|
tx_req.serialized.signature = None
|
||||||
|
tx_req.serialized.signature_index = None
|
||||||
|
tx_req.serialized.serialized_tx[:] = bytes()
|
||||||
gc.collect()
|
gc.collect()
|
||||||
if tx_hash is None:
|
if tx_hash is None:
|
||||||
return sanitize_tx_output(ack.tx, coin)
|
return sanitize_tx_output(ack.tx, coin)
|
||||||
@ -165,7 +173,9 @@ def request_tx_finish(tx_req: TxRequest) -> Awaitable[Any]: # type: ignore
|
|||||||
tx_req.request_type = TXFINISHED
|
tx_req.request_type = TXFINISHED
|
||||||
tx_req.details = None
|
tx_req.details = None
|
||||||
yield tx_req
|
yield tx_req
|
||||||
tx_req.serialized = None
|
tx_req.serialized.signature = None
|
||||||
|
tx_req.serialized.signature_index = None
|
||||||
|
tx_req.serialized.serialized_tx[:] = bytes()
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ _BIP32_CHANGE_CHAIN = const(1)
|
|||||||
# use and still allow to quickly brute-force the correct bip32 path
|
# use and still allow to quickly brute-force the correct bip32 path
|
||||||
_BIP32_MAX_LAST_ELEMENT = const(1000000)
|
_BIP32_MAX_LAST_ELEMENT = const(1000000)
|
||||||
|
|
||||||
|
# the number of bytes to preallocate for serialized transaction chunks
|
||||||
|
_MAX_SERIALIZED_CHUNK_SIZE = const(2048)
|
||||||
|
|
||||||
|
|
||||||
class SigningError(ValueError):
|
class SigningError(ValueError):
|
||||||
pass
|
pass
|
||||||
@ -98,15 +101,19 @@ class Bitcoin:
|
|||||||
# dict of booleans stating if input is segwit
|
# dict of booleans stating if input is segwit
|
||||||
self.segwit = {} # type: Dict[int, bool]
|
self.segwit = {} # type: Dict[int, bool]
|
||||||
|
|
||||||
|
# amounts
|
||||||
self.total_in = 0 # sum of input amounts
|
self.total_in = 0 # sum of input amounts
|
||||||
self.bip143_in = 0 # sum of segwit input amounts
|
self.bip143_in = 0 # sum of segwit input amounts
|
||||||
self.total_out = 0 # sum of output amounts
|
self.total_out = 0 # sum of output amounts
|
||||||
self.change_out = 0 # change output amount
|
self.change_out = 0 # change output amount
|
||||||
self.weight = tx_weight.TxWeightCalculator(tx.inputs_count, tx.outputs_count)
|
self.weight = tx_weight.TxWeightCalculator(tx.inputs_count, tx.outputs_count)
|
||||||
|
|
||||||
|
# transaction and signature serialization
|
||||||
|
self.serialized_tx = writers.empty_bytearray(_MAX_SERIALIZED_CHUNK_SIZE)
|
||||||
self.tx_req = TxRequest()
|
self.tx_req = TxRequest()
|
||||||
self.tx_req.details = TxRequestDetailsType()
|
self.tx_req.details = TxRequestDetailsType()
|
||||||
self.tx_ser = TxRequestSerializedType()
|
self.tx_req.serialized = TxRequestSerializedType()
|
||||||
|
self.tx_req.serialized.serialized_tx = self.serialized_tx
|
||||||
|
|
||||||
# h_confirmed is used to make sure that the inputs and outputs streamed for
|
# h_confirmed is used to make sure that the inputs and outputs streamed for
|
||||||
# confirmation in Steps 1 and 2 are the same as the ones streamed for signing
|
# confirmation in Steps 1 and 2 are the same as the ones streamed for signing
|
||||||
@ -180,11 +187,10 @@ class Bitcoin:
|
|||||||
await self.sign_segwit_input(i)
|
await self.sign_segwit_input(i)
|
||||||
elif any_segwit:
|
elif any_segwit:
|
||||||
# add empty witness for non-segwit inputs
|
# add empty witness for non-segwit inputs
|
||||||
self.tx_ser.serialized_tx.append(0)
|
self.serialized_tx.append(0)
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def step7_finish(self) -> None:
|
async def step7_finish(self) -> None:
|
||||||
self.write_sign_tx_footer(self.tx_ser.serialized_tx)
|
self.write_sign_tx_footer(self.serialized_tx)
|
||||||
await helpers.request_tx_finish(self.tx_req)
|
await helpers.request_tx_finish(self.tx_req)
|
||||||
|
|
||||||
async def process_input(self, i: int, txi: TxInputType) -> None:
|
async def process_input(self, i: int, txi: TxInputType) -> None:
|
||||||
@ -259,16 +265,9 @@ class Bitcoin:
|
|||||||
key_sign_pub = key_sign.public_key()
|
key_sign_pub = key_sign.public_key()
|
||||||
txi_sign.script_sig = self.input_derive_script(txi_sign, key_sign_pub)
|
txi_sign.script_sig = self.input_derive_script(txi_sign, key_sign_pub)
|
||||||
|
|
||||||
w_txi = writers.empty_bytearray(
|
|
||||||
7 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4
|
|
||||||
)
|
|
||||||
if i_sign == 0: # serializing first input => prepend headers
|
if i_sign == 0: # serializing first input => prepend headers
|
||||||
self.write_sign_tx_header(w_txi, True)
|
self.write_sign_tx_header(self.serialized_tx, True)
|
||||||
self.write_tx_input(w_txi, txi_sign)
|
self.write_tx_input(self.serialized_tx, txi_sign)
|
||||||
self.tx_ser.signature_index = None
|
|
||||||
self.tx_ser.signature = None
|
|
||||||
self.tx_ser.serialized_tx = w_txi
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def sign_segwit_input(self, i: int) -> None:
|
async def sign_segwit_input(self, i: int) -> None:
|
||||||
# STAGE_REQUEST_SEGWIT_WITNESS
|
# STAGE_REQUEST_SEGWIT_WITNESS
|
||||||
@ -297,18 +296,16 @@ class Bitcoin:
|
|||||||
if txi.multisig:
|
if txi.multisig:
|
||||||
# find out place of our signature based on the pubkey
|
# find out place of our signature based on the pubkey
|
||||||
signature_index = multisig.multisig_pubkey_index(txi.multisig, key_sign_pub)
|
signature_index = multisig.multisig_pubkey_index(txi.multisig, key_sign_pub)
|
||||||
witness = scripts.witness_p2wsh(
|
self.serialized_tx[:] = scripts.witness_p2wsh(
|
||||||
txi.multisig, signature, signature_index, self.get_hash_type()
|
txi.multisig, signature, signature_index, self.get_hash_type()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
witness = scripts.witness_p2wpkh(
|
self.serialized_tx[:] = scripts.witness_p2wpkh(
|
||||||
signature, key_sign_pub, self.get_hash_type()
|
signature, key_sign_pub, self.get_hash_type()
|
||||||
)
|
)
|
||||||
|
|
||||||
self.tx_ser.signature_index = i
|
self.tx_req.serialized.signature_index = i
|
||||||
self.tx_ser.signature = signature
|
self.tx_req.serialized.signature = signature
|
||||||
self.tx_ser.serialized_tx = witness
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def sign_nonsegwit_input(self, i_sign: int) -> None:
|
async def sign_nonsegwit_input(self, i_sign: int) -> None:
|
||||||
# hash of what we are signing with this input
|
# hash of what we are signing with this input
|
||||||
@ -381,17 +378,12 @@ class Bitcoin:
|
|||||||
txi_sign.script_sig = self.input_derive_script(
|
txi_sign.script_sig = self.input_derive_script(
|
||||||
txi_sign, key_sign_pub, signature
|
txi_sign, key_sign_pub, signature
|
||||||
)
|
)
|
||||||
w_txi_sign = writers.empty_bytearray(
|
|
||||||
5 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4
|
|
||||||
)
|
|
||||||
if i_sign == 0: # serializing first input => prepend headers
|
if i_sign == 0: # serializing first input => prepend headers
|
||||||
self.write_sign_tx_header(w_txi_sign, True in self.segwit.values())
|
self.write_sign_tx_header(self.serialized_tx, True in self.segwit.values())
|
||||||
self.write_tx_input(w_txi_sign, txi_sign)
|
self.write_tx_input(self.serialized_tx, txi_sign)
|
||||||
|
|
||||||
self.tx_ser.signature_index = i_sign
|
self.tx_req.serialized.signature_index = i_sign
|
||||||
self.tx_ser.signature = signature
|
self.tx_req.serialized.signature = signature
|
||||||
self.tx_ser.serialized_tx = w_txi_sign
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def serialize_output(self, i: int) -> None:
|
async def serialize_output(self, i: int) -> None:
|
||||||
# STAGE_REQUEST_5_OUTPUT
|
# STAGE_REQUEST_5_OUTPUT
|
||||||
@ -400,15 +392,9 @@ class Bitcoin:
|
|||||||
txo_bin.amount = txo.amount
|
txo_bin.amount = txo.amount
|
||||||
txo_bin.script_pubkey = self.output_derive_script(txo)
|
txo_bin.script_pubkey = self.output_derive_script(txo)
|
||||||
|
|
||||||
w_txo_bin = writers.empty_bytearray(5 + 8 + 5 + len(txo_bin.script_pubkey) + 4)
|
|
||||||
if i == 0: # serializing first output => prepend outputs count
|
if i == 0: # serializing first output => prepend outputs count
|
||||||
writers.write_varint(w_txo_bin, self.tx.outputs_count)
|
writers.write_varint(self.serialized_tx, self.tx.outputs_count)
|
||||||
writers.write_tx_output(w_txo_bin, txo_bin)
|
writers.write_tx_output(self.serialized_tx, txo_bin)
|
||||||
|
|
||||||
self.tx_ser.signature_index = None
|
|
||||||
self.tx_ser.signature = None
|
|
||||||
self.tx_ser.serialized_tx = w_txo_bin
|
|
||||||
self.tx_req.serialized = self.tx_ser
|
|
||||||
|
|
||||||
async def get_prevtx_output_value(self, prev_hash: bytes, prev_index: int) -> int:
|
async def get_prevtx_output_value(self, prev_hash: bytes, prev_index: int) -> int:
|
||||||
amount_out = 0 # output amount
|
amount_out = 0 # output amount
|
||||||
|
Loading…
Reference in New Issue
Block a user