mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
src/apps/wallet/sign_tx: add support for Decred
This commit is contained in:
parent
9e3ae93796
commit
7834d06aac
@ -1,6 +1,6 @@
|
||||
# generated from coininfo.py.mako
|
||||
# do not edit manually!
|
||||
from trezor.crypto.base58 import groestl512d_32, sha256d_32
|
||||
from trezor.crypto.base58 import blake256_32, groestl512d_32, sha256d_32
|
||||
|
||||
|
||||
class CoinInfo:
|
||||
@ -48,6 +48,9 @@ class CoinInfo:
|
||||
if curve_name == "secp256k1-groestl":
|
||||
self.b58_hash = groestl512d_32
|
||||
self.sign_hash_double = False
|
||||
elif curve_name == "secp256k1-decred":
|
||||
self.b58_hash = blake256_32
|
||||
self.sign_hash_double = False
|
||||
else:
|
||||
self.b58_hash = sha256d_32
|
||||
self.sign_hash_double = True
|
||||
|
@ -1,6 +1,6 @@
|
||||
# generated from coininfo.py.mako
|
||||
# do not edit manually!
|
||||
from trezor.crypto.base58 import groestl512d_32, sha256d_32
|
||||
from trezor.crypto.base58 import blake256_32, groestl512d_32, sha256d_32
|
||||
|
||||
|
||||
class CoinInfo:
|
||||
@ -48,6 +48,9 @@ class CoinInfo:
|
||||
if curve_name == "secp256k1-groestl":
|
||||
self.b58_hash = groestl512d_32
|
||||
self.sign_hash_double = False
|
||||
elif curve_name == "secp256k1-decred":
|
||||
self.b58_hash = blake256_32
|
||||
self.sign_hash_double = False
|
||||
else:
|
||||
self.b58_hash = sha256d_32
|
||||
self.sign_hash_double = True
|
||||
|
@ -14,6 +14,13 @@ def write_uint8(w: bytearray, n: int) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
def write_uint16_le(w: bytearray, n: int) -> int:
|
||||
assert 0 <= n <= 0xFFFF
|
||||
w.append(n & 0xFF)
|
||||
w.append((n >> 8) & 0xFF)
|
||||
return 2
|
||||
|
||||
|
||||
def write_uint32_le(w: bytearray, n: int) -> int:
|
||||
assert 0 <= n <= 0xFFFFFFFF
|
||||
w.append(n & 0xFF)
|
||||
|
@ -2,7 +2,7 @@ from micropython import const
|
||||
|
||||
from trezor.crypto import base58, bip32, cashaddr, der
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.crypto.hashlib import sha256
|
||||
from trezor.crypto.hashlib import blake256, sha256
|
||||
from trezor.messages import OutputScriptType
|
||||
from trezor.messages.TxRequestDetailsType import TxRequestDetailsType
|
||||
from trezor.messages.TxRequestSerializedType import TxRequestSerializedType
|
||||
@ -143,6 +143,14 @@ async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
|
||||
elif not await confirm_output(txo, coin):
|
||||
raise SigningError(FailureType.ActionCancelled, "Output cancelled")
|
||||
|
||||
if coin.decred:
|
||||
if txo.decred_script_version != 0:
|
||||
raise SigningError(
|
||||
FailureType.ActionCancelled,
|
||||
"Cannot send to output with script version != 0",
|
||||
)
|
||||
txo_bin.decred_script_version = txo.decred_script_version
|
||||
|
||||
write_tx_output(h_first, txo_bin)
|
||||
hash143.add_output(txo_bin)
|
||||
total_out += txo_bin.amount
|
||||
@ -183,6 +191,20 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
||||
tx_req.details = TxRequestDetailsType()
|
||||
tx_req.serialized = None
|
||||
|
||||
h_prefix_sign = None
|
||||
if coin.decred:
|
||||
h_prefix_sign = HashWriter(blake256)
|
||||
|
||||
# used to validate no changes between check_tx_fee and phase 2
|
||||
h_second = HashWriter(sha256)
|
||||
|
||||
# used to validate no changes between phase 2 and decred witness
|
||||
h_third = HashWriter(sha256)
|
||||
h_fourth = HashWriter(sha256)
|
||||
write_uint16(h_prefix_sign, tx.version)
|
||||
write_uint16(h_prefix_sign, 1) # serType
|
||||
write_varint(h_prefix_sign, tx.inputs_count)
|
||||
|
||||
for i_sign in range(tx.inputs_count):
|
||||
progress.advance()
|
||||
txi_sign = None
|
||||
@ -259,6 +281,22 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
||||
|
||||
tx_req.serialized = tx_ser
|
||||
|
||||
elif coin.decred:
|
||||
txi_sign = await request_tx_input(tx_req, i_sign)
|
||||
|
||||
input_check_wallet_path(txi_sign, wallet_path)
|
||||
|
||||
w_txi = empty_bytearray(7 + len(txi_sign.prev_hash) + 9)
|
||||
if i_sign == 0: # serializing first input => prepend headers
|
||||
write_bytes(w_txi, get_tx_header(coin, tx, False))
|
||||
|
||||
write_tx_input_decred(w_txi, txi_sign)
|
||||
write_tx_input_decred(h_prefix_sign, txi_sign)
|
||||
tx_ser.serialized_tx = w_txi
|
||||
tx_req.serialized = tx_ser
|
||||
write_tx_input_check(h_second, txi_sign)
|
||||
write_tx_input_check(h_third, txi_sign)
|
||||
|
||||
else:
|
||||
# hash of what we are signing with this input
|
||||
h_sign = HashWriter(sha256)
|
||||
@ -360,13 +398,19 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
||||
# STAGE_REQUEST_5_OUTPUT
|
||||
txo = await request_tx_output(tx_req, o)
|
||||
txo_bin.amount = txo.amount
|
||||
txo_bin.decred_script_version = txo.decred_script_version
|
||||
txo_bin.script_pubkey = output_derive_script(txo, coin, root)
|
||||
|
||||
# serialize output
|
||||
w_txo_bin = empty_bytearray(5 + 8 + 5 + len(txo_bin.script_pubkey) + 4)
|
||||
if o == 0: # serializing first output => prepend outputs count
|
||||
write_varint(w_txo_bin, tx.outputs_count)
|
||||
if coin.decred:
|
||||
write_varint(h_prefix_sign, tx.outputs_count)
|
||||
write_tx_output(w_txo_bin, txo_bin)
|
||||
if coin.decred:
|
||||
write_tx_output(h_prefix_sign, txo_bin)
|
||||
write_tx_output(h_second, txo_bin)
|
||||
|
||||
tx_ser.signature_index = None
|
||||
tx_ser.signature = None
|
||||
@ -376,6 +420,18 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
||||
|
||||
any_segwit = True in segwit.values()
|
||||
|
||||
prefix_hash = None
|
||||
if coin.decred:
|
||||
if get_tx_hash(h_first, False) != get_tx_hash(h_second):
|
||||
raise SigningError(
|
||||
FailureType.ProcessError, "Transaction has changed during signing"
|
||||
)
|
||||
write_uint32(h_prefix_sign, tx.lock_time)
|
||||
write_uint32(h_prefix_sign, tx.expiry)
|
||||
prefix_hash = get_tx_hash(
|
||||
h_prefix_sign, double=coin.sign_hash_double, reverse=False
|
||||
)
|
||||
|
||||
for i in range(tx.inputs_count):
|
||||
progress.advance()
|
||||
if segwit[i]:
|
||||
@ -416,10 +472,75 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
||||
tx_ser.serialized_tx = bytearray(1) # empty witness for non-segwit inputs
|
||||
tx_ser.signature_index = None
|
||||
tx_ser.signature = None
|
||||
elif coin.decred:
|
||||
txi = await request_tx_input(tx_req, i)
|
||||
input_check_wallet_path(txi, wallet_path)
|
||||
|
||||
write_tx_input_check(h_fourth, txi)
|
||||
|
||||
if txi.amount > authorized_in:
|
||||
raise SigningError(
|
||||
FailureType.ProcessError, "Transaction has changed during signing"
|
||||
)
|
||||
authorized_in -= txi.amount
|
||||
|
||||
key_sign = node_derive(root, txi.address_n)
|
||||
key_sign_pub = key_sign.public_key()
|
||||
prev_txo = TxOutputType(
|
||||
address_n=txi.address_n, script_type=OutputScriptType.PAYTOADDRESS
|
||||
)
|
||||
prev_pkscript = output_derive_script(prev_txo, coin, root)
|
||||
|
||||
h_witness = HashWriter(blake256)
|
||||
write_uint16(h_witness, tx.version)
|
||||
write_uint16(h_witness, 3) # serType serializeWitness
|
||||
write_varint(h_witness, tx.inputs_count)
|
||||
|
||||
for ii in range(tx.inputs_count):
|
||||
if ii == i:
|
||||
write_varint(h_witness, len(prev_pkscript))
|
||||
write_bytes(h_witness, prev_pkscript)
|
||||
else:
|
||||
write_varint(h_witness, 0)
|
||||
|
||||
witness_hash = get_tx_hash(
|
||||
h_witness, double=coin.sign_hash_double, reverse=False
|
||||
)
|
||||
|
||||
h_sign = HashWriter(blake256)
|
||||
write_uint32(h_sign, 1) # SIGHASHALL
|
||||
write_bytes(h_sign, prefix_hash)
|
||||
write_bytes(h_sign, witness_hash)
|
||||
|
||||
sig_hash = get_tx_hash(h_sign, double=coin.sign_hash_double)
|
||||
signature = ecdsa_sign(key_sign, sig_hash)
|
||||
tx_ser.signature_index = i_sign
|
||||
tx_ser.signature = signature
|
||||
|
||||
# serialize input with correct signature
|
||||
txi.script_sig = input_derive_script(coin, txi, key_sign_pub, signature)
|
||||
w_txi_sign = empty_bytearray(
|
||||
10 + len(txi.prev_hash) + 18 + len(txi.script_sig)
|
||||
)
|
||||
|
||||
if i == 0:
|
||||
write_uint32(w_txi_sign, tx.lock_time)
|
||||
write_uint32(w_txi_sign, tx.expiry)
|
||||
write_varint(w_txi_sign, tx.inputs_count)
|
||||
|
||||
write_tx_input_decred_witness(w_txi_sign, txi)
|
||||
tx_ser.serialized_tx = w_txi_sign
|
||||
|
||||
tx_req.serialized = tx_ser
|
||||
|
||||
write_uint32(tx_ser.serialized_tx, tx.lock_time)
|
||||
if coin.decred:
|
||||
if get_tx_hash(h_third, False) != get_tx_hash(h_fourth):
|
||||
raise SigningError(
|
||||
FailureType.ProcessError, "Transaction has changed during signing"
|
||||
)
|
||||
else:
|
||||
write_uint32(tx_ser.serialized_tx, tx.lock_time)
|
||||
|
||||
if tx.overwintered:
|
||||
write_uint32(tx_ser.serialized_tx, tx.expiry) # expiryHeight
|
||||
write_varint(tx_ser.serialized_tx, 0) # nJoinSplit
|
||||
@ -435,11 +556,17 @@ async def get_prevtx_output_value(
|
||||
# STAGE_REQUEST_2_PREV_META
|
||||
tx = await request_tx_meta(tx_req, prev_hash)
|
||||
|
||||
txh = HashWriter(sha256)
|
||||
if coin.decred:
|
||||
txh = HashWriter(blake256)
|
||||
else:
|
||||
txh = HashWriter(sha256)
|
||||
|
||||
if tx.overwintered:
|
||||
write_uint32(txh, tx.version | OVERWINTERED) # nVersion | fOverwintered
|
||||
write_uint32(txh, coin.version_group_id) # nVersionGroupId
|
||||
elif coin.decred:
|
||||
write_uint16(txh, tx.version)
|
||||
write_uint16(txh, 1) # serType
|
||||
else:
|
||||
write_uint32(txh, tx.version) # nVersion
|
||||
|
||||
@ -448,7 +575,10 @@ async def get_prevtx_output_value(
|
||||
for i in range(tx.inputs_cnt):
|
||||
# STAGE_REQUEST_2_PREV_INPUT
|
||||
txi = await request_tx_input(tx_req, i, prev_hash)
|
||||
write_tx_input(txh, txi)
|
||||
if coin.decred:
|
||||
write_tx_input_decred(txh, txi)
|
||||
else:
|
||||
write_tx_input(txh, txi)
|
||||
|
||||
write_varint(txh, tx.outputs_cnt)
|
||||
|
||||
@ -458,10 +588,15 @@ async def get_prevtx_output_value(
|
||||
write_tx_output(txh, txo_bin)
|
||||
if o == prev_index:
|
||||
total_out += txo_bin.amount
|
||||
if coin.decred and txo_bin.decred_script_version != 0:
|
||||
raise SigningError(
|
||||
FailureType.ProcessError,
|
||||
"Cannot use utxo that has script_version != 0",
|
||||
)
|
||||
|
||||
write_uint32(txh, tx.lock_time)
|
||||
|
||||
if tx.overwintered:
|
||||
if tx.overwintered or coin.decred:
|
||||
write_uint32(txh, tx.expiry)
|
||||
|
||||
ofs = 0
|
||||
|
@ -5,10 +5,13 @@ from trezor.messages.TxOutputBinType import TxOutputBinType
|
||||
from apps.common.writers import (
|
||||
write_bytes,
|
||||
write_bytes_reversed,
|
||||
write_uint8,
|
||||
write_uint16_le,
|
||||
write_uint32_le,
|
||||
write_uint64_le,
|
||||
)
|
||||
|
||||
write_uint16 = write_uint16_le
|
||||
write_uint32 = write_uint32_le
|
||||
write_uint64 = write_uint64_le
|
||||
|
||||
@ -32,8 +35,25 @@ def write_tx_input_check(w, i: TxInputType):
|
||||
write_uint32(w, i.amount or 0)
|
||||
|
||||
|
||||
def write_tx_input_decred(w, i: TxInputType):
|
||||
write_bytes_reversed(w, i.prev_hash)
|
||||
write_uint32(w, i.prev_index)
|
||||
write_uint8(w, i.decred_tree)
|
||||
write_uint32(w, i.sequence)
|
||||
|
||||
|
||||
def write_tx_input_decred_witness(w, i: TxInputType):
|
||||
write_uint64(w, i.amount)
|
||||
write_uint32(w, 0) # block height fraud proof
|
||||
write_uint32(w, 0xFFFFFFFF) # block index fraud proof
|
||||
write_varint(w, len(i.script_sig))
|
||||
write_bytes(w, i.script_sig)
|
||||
|
||||
|
||||
def write_tx_output(w, o: TxOutputBinType):
|
||||
write_uint64(w, o.amount)
|
||||
if o.decred_script_version is not None:
|
||||
write_uint16_le(w, o.decred_script_version)
|
||||
write_varint(w, len(o.script_pubkey))
|
||||
write_bytes(w, o.script_pubkey)
|
||||
|
||||
|
@ -71,6 +71,12 @@ def groestl512d_32(data: bytes) -> bytes:
|
||||
return groestl512(groestl512(data).digest()).digest()[:4]
|
||||
|
||||
|
||||
def blake256_32(data: bytes) -> bytes:
|
||||
from .hashlib import blake256
|
||||
|
||||
return blake256(blake256(data).digest()).digest()[:4]
|
||||
|
||||
|
||||
def encode_check(data: bytes, digestfunc=sha256d_32) -> str:
|
||||
"""
|
||||
Convert bytes to base58 encoded string, append checksum.
|
||||
|
Loading…
Reference in New Issue
Block a user