mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 17:38:39 +00:00
src/apps/wallet/sign_tx: re-enable Decred
This commit is contained in:
parent
de12ad705c
commit
2277a9c754
@ -1,6 +1,6 @@
|
|||||||
# generated from coininfo.py.mako
|
# generated from coininfo.py.mako
|
||||||
# do not edit manually!
|
# 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:
|
class CoinInfo:
|
||||||
@ -48,6 +48,9 @@ class CoinInfo:
|
|||||||
if curve_name == "secp256k1-groestl":
|
if curve_name == "secp256k1-groestl":
|
||||||
self.b58_hash = groestl512d_32
|
self.b58_hash = groestl512d_32
|
||||||
self.sign_hash_double = False
|
self.sign_hash_double = False
|
||||||
|
elif curve_name == "secp256k1-decred":
|
||||||
|
self.b58_hash = blake256_32
|
||||||
|
self.sign_hash_double = False
|
||||||
else:
|
else:
|
||||||
self.b58_hash = sha256d_32
|
self.b58_hash = sha256d_32
|
||||||
self.sign_hash_double = True
|
self.sign_hash_double = True
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# generated from coininfo.py.mako
|
# generated from coininfo.py.mako
|
||||||
# do not edit manually!
|
# 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:
|
class CoinInfo:
|
||||||
@ -48,6 +48,9 @@ class CoinInfo:
|
|||||||
if curve_name == "secp256k1-groestl":
|
if curve_name == "secp256k1-groestl":
|
||||||
self.b58_hash = groestl512d_32
|
self.b58_hash = groestl512d_32
|
||||||
self.sign_hash_double = False
|
self.sign_hash_double = False
|
||||||
|
elif curve_name == "secp256k1-decred":
|
||||||
|
self.b58_hash = blake256_32
|
||||||
|
self.sign_hash_double = False
|
||||||
else:
|
else:
|
||||||
self.b58_hash = sha256d_32
|
self.b58_hash = sha256d_32
|
||||||
self.sign_hash_double = True
|
self.sign_hash_double = True
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
from ubinascii import hexlify
|
from ubinascii import hexlify
|
||||||
|
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import blake256, sha256
|
||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from apps.wallet.sign_tx.signing import write_varint
|
from apps.wallet.sign_tx.signing import write_varint
|
||||||
|
|
||||||
|
|
||||||
def message_digest(coin, message):
|
def message_digest(coin, message):
|
||||||
|
if coin.decred:
|
||||||
|
h = HashWriter(blake256)
|
||||||
|
else:
|
||||||
h = HashWriter(sha256)
|
h = HashWriter(sha256)
|
||||||
write_varint(h, len(coin.signed_message_header))
|
write_varint(h, len(coin.signed_message_header))
|
||||||
h.extend(coin.signed_message_header)
|
h.extend(coin.signed_message_header)
|
||||||
|
@ -14,6 +14,13 @@ def write_uint8(w: bytearray, n: int) -> int:
|
|||||||
return 1
|
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:
|
def write_uint32_le(w: bytearray, n: int) -> int:
|
||||||
assert 0 <= n <= 0xFFFFFFFF
|
assert 0 <= n <= 0xFFFFFFFF
|
||||||
w.append(n & 0xFF)
|
w.append(n & 0xFF)
|
||||||
|
@ -9,6 +9,7 @@ from apps.common import address_type
|
|||||||
from apps.common.coininfo import CoinInfo
|
from apps.common.coininfo import CoinInfo
|
||||||
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys, multisig_pubkey_index
|
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys, multisig_pubkey_index
|
||||||
from apps.wallet.sign_tx.scripts import (
|
from apps.wallet.sign_tx.scripts import (
|
||||||
|
blake256_ripemd160_digest,
|
||||||
output_script_multisig,
|
output_script_multisig,
|
||||||
output_script_native_p2wpkh_or_p2wsh,
|
output_script_native_p2wpkh_or_p2wsh,
|
||||||
sha256_ripemd160_digest,
|
sha256_ripemd160_digest,
|
||||||
@ -65,7 +66,7 @@ def get_address(
|
|||||||
return address_multisig_p2wsh(pubkeys, multisig.m, coin.bech32_prefix)
|
return address_multisig_p2wsh(pubkeys, multisig.m, coin.bech32_prefix)
|
||||||
|
|
||||||
# native p2wpkh
|
# native p2wpkh
|
||||||
return address_p2wpkh(node.public_key(), coin.bech32_prefix)
|
return address_p2wpkh(node.public_key(), coin)
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
script_type == InputScriptType.SPENDP2SHWITNESS
|
script_type == InputScriptType.SPENDP2SHWITNESS
|
||||||
@ -92,6 +93,9 @@ def address_multisig_p2sh(pubkeys: bytes, m: int, coin: CoinInfo):
|
|||||||
FailureType.ProcessError, "Multisig not enabled on this coin"
|
FailureType.ProcessError, "Multisig not enabled on this coin"
|
||||||
)
|
)
|
||||||
redeem_script = output_script_multisig(pubkeys, m)
|
redeem_script = output_script_multisig(pubkeys, m)
|
||||||
|
if coin.decred:
|
||||||
|
redeem_script_hash = blake256_ripemd160_digest(redeem_script)
|
||||||
|
else:
|
||||||
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
||||||
return address_p2sh(redeem_script_hash, coin)
|
return address_p2sh(redeem_script_hash, coin)
|
||||||
|
|
||||||
@ -127,7 +131,7 @@ def address_p2sh(redeem_script_hash: bytes, coin: CoinInfo) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def address_p2wpkh_in_p2sh(pubkey: bytes, coin: CoinInfo) -> str:
|
def address_p2wpkh_in_p2sh(pubkey: bytes, coin: CoinInfo) -> str:
|
||||||
pubkey_hash = ecdsa_hash_pubkey(pubkey)
|
pubkey_hash = ecdsa_hash_pubkey(pubkey, coin)
|
||||||
redeem_script = output_script_native_p2wpkh_or_p2wsh(pubkey_hash)
|
redeem_script = output_script_native_p2wpkh_or_p2wsh(pubkey_hash)
|
||||||
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
||||||
return address_p2sh(redeem_script_hash, coin)
|
return address_p2sh(redeem_script_hash, coin)
|
||||||
@ -139,9 +143,9 @@ def address_p2wsh_in_p2sh(witness_script_hash: bytes, coin: CoinInfo) -> str:
|
|||||||
return address_p2sh(redeem_script_hash, coin)
|
return address_p2sh(redeem_script_hash, coin)
|
||||||
|
|
||||||
|
|
||||||
def address_p2wpkh(pubkey: bytes, hrp: str) -> str:
|
def address_p2wpkh(pubkey: bytes, coin: CoinInfo) -> str:
|
||||||
pubkeyhash = ecdsa_hash_pubkey(pubkey)
|
pubkeyhash = ecdsa_hash_pubkey(pubkey, coin)
|
||||||
address = bech32.encode(hrp, _BECH32_WITVER, pubkeyhash)
|
address = bech32.encode(coin.bech32_prefix, _BECH32_WITVER, pubkeyhash)
|
||||||
if address is None:
|
if address is None:
|
||||||
raise AddressError(FailureType.ProcessError, "Invalid address")
|
raise AddressError(FailureType.ProcessError, "Invalid address")
|
||||||
return address
|
return address
|
||||||
@ -173,13 +177,17 @@ def address_to_cashaddr(address: str, coin: CoinInfo) -> str:
|
|||||||
return cashaddr.encode(coin.cashaddr_prefix, version, data)
|
return cashaddr.encode(coin.cashaddr_prefix, version, data)
|
||||||
|
|
||||||
|
|
||||||
def ecdsa_hash_pubkey(pubkey: bytes) -> bytes:
|
def ecdsa_hash_pubkey(pubkey: bytes, coin: CoinInfo) -> bytes:
|
||||||
if pubkey[0] == 0x04:
|
if pubkey[0] == 0x04:
|
||||||
ensure(len(pubkey) == 65) # uncompressed format
|
ensure(len(pubkey) == 65) # uncompressed format
|
||||||
elif pubkey[0] == 0x00:
|
elif pubkey[0] == 0x00:
|
||||||
ensure(len(pubkey) == 1) # point at infinity
|
ensure(len(pubkey) == 1) # point at infinity
|
||||||
else:
|
else:
|
||||||
ensure(len(pubkey) == 33) # compresssed format
|
ensure(len(pubkey) == 33) # compresssed format
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
return blake256_ripemd160_digest(pubkey)
|
||||||
|
|
||||||
h = sha256(pubkey).digest()
|
h = sha256(pubkey).digest()
|
||||||
h = ripemd160(h).digest()
|
h = ripemd160(h).digest()
|
||||||
return h
|
return h
|
||||||
|
65
src/apps/wallet/sign_tx/decred_prefix_hasher.py
Normal file
65
src/apps/wallet/sign_tx/decred_prefix_hasher.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
from micropython import const
|
||||||
|
|
||||||
|
from trezor.crypto.hashlib import blake256
|
||||||
|
from trezor.messages.SignTx import SignTx
|
||||||
|
from trezor.messages.TxInputType import TxInputType
|
||||||
|
from trezor.messages.TxOutputBinType import TxOutputBinType
|
||||||
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
|
from apps.wallet.sign_tx.writers import (
|
||||||
|
write_tx_input_decred,
|
||||||
|
write_tx_output,
|
||||||
|
write_uint32,
|
||||||
|
write_varint,
|
||||||
|
)
|
||||||
|
|
||||||
|
DECRED_SERIALIZE_FULL = const(0 << 16)
|
||||||
|
DECRED_SERIALIZE_NO_WITNESS = const(1 << 16)
|
||||||
|
DECRED_SERIALIZE_WITNESS_SIGNING = const(3 << 16)
|
||||||
|
|
||||||
|
DECRED_SIGHASHALL = const(1)
|
||||||
|
|
||||||
|
|
||||||
|
class DecredPrefixHasher:
|
||||||
|
"""
|
||||||
|
While Decred does not have the exact same implementation as bip143/zip143,
|
||||||
|
the semantics for using the prefix hash of transactions are close enough
|
||||||
|
that a pseudo-bip143 class can be used to store the prefix hash during the
|
||||||
|
check_fee stage of transaction signature to then reuse it at the sign_tx
|
||||||
|
stage without having to request the inputs again.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, tx: SignTx):
|
||||||
|
self.h_prefix = HashWriter(blake256)
|
||||||
|
self.last_output_bytes = None
|
||||||
|
write_uint32(self.h_prefix, tx.version | DECRED_SERIALIZE_NO_WITNESS)
|
||||||
|
write_varint(self.h_prefix, tx.inputs_count)
|
||||||
|
|
||||||
|
def add_prevouts(self, txi: TxInputType):
|
||||||
|
write_tx_input_decred(self.h_prefix, txi)
|
||||||
|
|
||||||
|
def add_sequence(self, txi: TxInputType):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_output_count(self, tx: SignTx):
|
||||||
|
write_varint(self.h_prefix, tx.outputs_count)
|
||||||
|
|
||||||
|
def add_output(self, txo_bin: TxOutputBinType):
|
||||||
|
write_tx_output(self.h_prefix, txo_bin)
|
||||||
|
|
||||||
|
def set_last_output_bytes(self, w_txo_bin: bytearray):
|
||||||
|
"""
|
||||||
|
This is required because the last serialized output obtained in
|
||||||
|
`check_fee` will only be sent to the client in `sign_tx`
|
||||||
|
"""
|
||||||
|
self.last_output_bytes = w_txo_bin
|
||||||
|
|
||||||
|
def get_last_output_bytes(self):
|
||||||
|
return self.last_output_bytes
|
||||||
|
|
||||||
|
def add_locktime_expiry(self, tx: SignTx):
|
||||||
|
write_uint32(self.h_prefix, tx.lock_time)
|
||||||
|
write_uint32(self.h_prefix, tx.expiry)
|
||||||
|
|
||||||
|
def prefix_hash(self) -> bytes:
|
||||||
|
return self.h_prefix.get_digest()
|
@ -1,6 +1,7 @@
|
|||||||
from trezor.crypto.hashlib import ripemd160, sha256
|
from trezor.crypto.hashlib import blake256, ripemd160, sha256
|
||||||
from trezor.messages.MultisigRedeemScriptType import MultisigRedeemScriptType
|
from trezor.messages.MultisigRedeemScriptType import MultisigRedeemScriptType
|
||||||
|
|
||||||
|
from apps.common.coininfo import CoinInfo
|
||||||
from apps.common.writers import empty_bytearray
|
from apps.common.writers import empty_bytearray
|
||||||
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys
|
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys
|
||||||
from apps.wallet.sign_tx.writers import (
|
from apps.wallet.sign_tx.writers import (
|
||||||
@ -192,6 +193,7 @@ def input_script_multisig(
|
|||||||
signature: bytes,
|
signature: bytes,
|
||||||
signature_index: int,
|
signature_index: int,
|
||||||
sighash: int,
|
sighash: int,
|
||||||
|
coin: CoinInfo,
|
||||||
):
|
):
|
||||||
signatures = multisig.signatures # other signatures
|
signatures = multisig.signatures # other signatures
|
||||||
if len(signatures[signature_index]) > 0:
|
if len(signatures[signature_index]) > 0:
|
||||||
@ -199,6 +201,8 @@ def input_script_multisig(
|
|||||||
signatures[signature_index] = signature # our signature
|
signatures[signature_index] = signature # our signature
|
||||||
|
|
||||||
w = bytearray()
|
w = bytearray()
|
||||||
|
|
||||||
|
if not coin.decred:
|
||||||
# Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which
|
# Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which
|
||||||
# consumes one additional item on the stack:
|
# consumes one additional item on the stack:
|
||||||
# https://bitcoin.org/en/developer-guide#standard-transactions
|
# https://bitcoin.org/en/developer-guide#standard-transactions
|
||||||
@ -267,3 +271,9 @@ def sha256_ripemd160_digest(b: bytes) -> bytes:
|
|||||||
h = sha256(b).digest()
|
h = sha256(b).digest()
|
||||||
h = ripemd160(h).digest()
|
h = ripemd160(h).digest()
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
def blake256_ripemd160_digest(b: bytes) -> bytes:
|
||||||
|
h = blake256(b).digest()
|
||||||
|
h = ripemd160(h).digest()
|
||||||
|
return h
|
||||||
|
@ -2,7 +2,7 @@ from micropython import const
|
|||||||
|
|
||||||
from trezor.crypto import base58, bip32, cashaddr, der
|
from trezor.crypto import base58, bip32, cashaddr, der
|
||||||
from trezor.crypto.curve import secp256k1
|
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 import OutputScriptType
|
||||||
from trezor.messages.TxRequestDetailsType import TxRequestDetailsType
|
from trezor.messages.TxRequestDetailsType import TxRequestDetailsType
|
||||||
from trezor.messages.TxRequestSerializedType import TxRequestSerializedType
|
from trezor.messages.TxRequestSerializedType import TxRequestSerializedType
|
||||||
@ -13,6 +13,12 @@ from apps.common.coininfo import CoinInfo
|
|||||||
from apps.common.writers import empty_bytearray
|
from apps.common.writers import empty_bytearray
|
||||||
from apps.wallet.sign_tx import progress
|
from apps.wallet.sign_tx import progress
|
||||||
from apps.wallet.sign_tx.addresses import *
|
from apps.wallet.sign_tx.addresses import *
|
||||||
|
from apps.wallet.sign_tx.decred_prefix_hasher import (
|
||||||
|
DECRED_SERIALIZE_NO_WITNESS,
|
||||||
|
DECRED_SERIALIZE_WITNESS_SIGNING,
|
||||||
|
DECRED_SIGHASHALL,
|
||||||
|
DecredPrefixHasher,
|
||||||
|
)
|
||||||
from apps.wallet.sign_tx.helpers import *
|
from apps.wallet.sign_tx.helpers import *
|
||||||
from apps.wallet.sign_tx.multisig import *
|
from apps.wallet.sign_tx.multisig import *
|
||||||
from apps.wallet.sign_tx.overwinter_zip143 import ( # noqa:F401
|
from apps.wallet.sign_tx.overwinter_zip143 import ( # noqa:F401
|
||||||
@ -59,7 +65,10 @@ async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
|
|||||||
# tx, as the SignTx info is streamed only once
|
# tx, as the SignTx info is streamed only once
|
||||||
h_first = HashWriter(sha256) # not a real tx hash
|
h_first = HashWriter(sha256) # not a real tx hash
|
||||||
|
|
||||||
if tx.overwintered:
|
if coin.decred:
|
||||||
|
hash143 = DecredPrefixHasher(tx) # pseudo bip143 prefix hashing
|
||||||
|
tx_ser = TxRequestSerializedType()
|
||||||
|
elif tx.overwintered:
|
||||||
hash143 = Zip143() # zip143 transaction hashing
|
hash143 = Zip143() # zip143 transaction hashing
|
||||||
else:
|
else:
|
||||||
hash143 = Bip143() # bip143 transaction hashing
|
hash143 = Bip143() # bip143 transaction hashing
|
||||||
@ -130,6 +139,17 @@ async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
|
|||||||
else:
|
else:
|
||||||
raise SigningError(FailureType.DataError, "Wrong input script type")
|
raise SigningError(FailureType.DataError, "Wrong input script type")
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
w_txi = empty_bytearray(8 if i == 0 else 0 + 9 + len(txi.prev_hash))
|
||||||
|
if i == 0: # serializing first input => prepend headers
|
||||||
|
write_bytes(w_txi, get_tx_header(coin, tx))
|
||||||
|
write_tx_input_decred(w_txi, txi)
|
||||||
|
tx_ser.serialized_tx = w_txi
|
||||||
|
tx_req.serialized = tx_ser
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
hash143.add_output_count(tx)
|
||||||
|
|
||||||
for o in range(tx.outputs_count):
|
for o in range(tx.outputs_count):
|
||||||
# STAGE_REQUEST_3_OUTPUT
|
# STAGE_REQUEST_3_OUTPUT
|
||||||
txo = await request_tx_output(tx_req, o)
|
txo = await request_tx_output(tx_req, o)
|
||||||
@ -143,6 +163,22 @@ async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
|
|||||||
elif not await confirm_output(txo, coin):
|
elif not await confirm_output(txo, coin):
|
||||||
raise SigningError(FailureType.ActionCancelled, "Output cancelled")
|
raise SigningError(FailureType.ActionCancelled, "Output cancelled")
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
if txo.decred_script_version is not None and 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
|
||||||
|
|
||||||
|
w_txo_bin = empty_bytearray(4 + 8 + 2 + 4 + len(txo_bin.script_pubkey))
|
||||||
|
if o == 0: # serializing first output => prepend outputs count
|
||||||
|
write_varint(w_txo_bin, tx.outputs_count)
|
||||||
|
write_tx_output(w_txo_bin, txo_bin)
|
||||||
|
tx_ser.serialized_tx = w_txo_bin
|
||||||
|
tx_req.serialized = tx_ser
|
||||||
|
hash143.set_last_output_bytes(w_txo_bin)
|
||||||
|
|
||||||
write_tx_output(h_first, txo_bin)
|
write_tx_output(h_first, txo_bin)
|
||||||
hash143.add_output(txo_bin)
|
hash143.add_output(txo_bin)
|
||||||
total_out += txo_bin.amount
|
total_out += txo_bin.amount
|
||||||
@ -159,6 +195,9 @@ async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
|
|||||||
if not await confirm_total(total_in - change_out, fee, coin):
|
if not await confirm_total(total_in - change_out, fee, coin):
|
||||||
raise SigningError(FailureType.ActionCancelled, "Total cancelled")
|
raise SigningError(FailureType.ActionCancelled, "Total cancelled")
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
hash143.add_locktime_expiry(tx)
|
||||||
|
|
||||||
return h_first, hash143, segwit, total_in, wallet_path
|
return h_first, hash143, segwit, total_in, wallet_path
|
||||||
|
|
||||||
|
|
||||||
@ -183,6 +222,9 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
tx_req.details = TxRequestDetailsType()
|
tx_req.details = TxRequestDetailsType()
|
||||||
tx_req.serialized = None
|
tx_req.serialized = None
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
prefix_hash = hash143.prefix_hash()
|
||||||
|
|
||||||
for i_sign in range(tx.inputs_count):
|
for i_sign in range(tx.inputs_count):
|
||||||
progress.advance()
|
progress.advance()
|
||||||
txi_sign = None
|
txi_sign = None
|
||||||
@ -234,7 +276,11 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
key_sign = node_derive(root, txi_sign.address_n)
|
key_sign = node_derive(root, txi_sign.address_n)
|
||||||
key_sign_pub = key_sign.public_key()
|
key_sign_pub = key_sign.public_key()
|
||||||
hash143_hash = hash143.preimage_hash(
|
hash143_hash = hash143.preimage_hash(
|
||||||
coin, tx, txi_sign, ecdsa_hash_pubkey(key_sign_pub), get_hash_type(coin)
|
coin,
|
||||||
|
tx,
|
||||||
|
txi_sign,
|
||||||
|
ecdsa_hash_pubkey(key_sign_pub, coin),
|
||||||
|
get_hash_type(coin),
|
||||||
)
|
)
|
||||||
|
|
||||||
# if multisig, check if singing with a key that is included in multisig
|
# if multisig, check if singing with a key that is included in multisig
|
||||||
@ -259,6 +305,70 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
|
|
||||||
tx_req.serialized = tx_ser
|
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)
|
||||||
|
|
||||||
|
key_sign = node_derive(root, txi_sign.address_n)
|
||||||
|
key_sign_pub = key_sign.public_key()
|
||||||
|
|
||||||
|
if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
|
||||||
|
prev_pkscript = output_script_multisig(
|
||||||
|
multisig_get_pubkeys(txi_sign.multisig), txi_sign.multisig.m
|
||||||
|
)
|
||||||
|
elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
|
||||||
|
prev_pkscript = output_script_p2pkh(
|
||||||
|
ecdsa_hash_pubkey(key_sign_pub, coin)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown input script type")
|
||||||
|
|
||||||
|
h_witness = HashWriter(blake256)
|
||||||
|
write_uint32(h_witness, tx.version | DECRED_SERIALIZE_WITNESS_SIGNING)
|
||||||
|
write_varint(h_witness, tx.inputs_count)
|
||||||
|
|
||||||
|
for ii in range(tx.inputs_count):
|
||||||
|
if ii == i_sign:
|
||||||
|
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, DECRED_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_sign.script_sig = input_derive_script(
|
||||||
|
coin, txi_sign, key_sign_pub, signature
|
||||||
|
)
|
||||||
|
w_txi_sign = empty_bytearray(
|
||||||
|
8 + 4 + len(hash143.get_last_output_bytes())
|
||||||
|
if i_sign == 0
|
||||||
|
else 0 + 16 + 4 + len(txi_sign.script_sig)
|
||||||
|
)
|
||||||
|
|
||||||
|
if i_sign == 0:
|
||||||
|
write_bytes(w_txi_sign, hash143.get_last_output_bytes())
|
||||||
|
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_sign)
|
||||||
|
tx_ser.serialized_tx = w_txi_sign
|
||||||
|
tx_req.serialized = tx_ser
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# hash of what we are signing with this input
|
# hash of what we are signing with this input
|
||||||
h_sign = HashWriter(sha256)
|
h_sign = HashWriter(sha256)
|
||||||
@ -292,7 +402,7 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
)
|
)
|
||||||
elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
|
elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
|
||||||
txi_sign.script_sig = output_script_p2pkh(
|
txi_sign.script_sig = output_script_p2pkh(
|
||||||
ecdsa_hash_pubkey(key_sign_pub)
|
ecdsa_hash_pubkey(key_sign_pub, coin)
|
||||||
)
|
)
|
||||||
if coin.bip115:
|
if coin.bip115:
|
||||||
txi_sign.script_sig += script_replay_protection_bip115(
|
txi_sign.script_sig += script_replay_protection_bip115(
|
||||||
@ -355,6 +465,9 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
|
|
||||||
tx_req.serialized = tx_ser
|
tx_req.serialized = tx_ser
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
return await request_tx_finish(tx_req)
|
||||||
|
|
||||||
for o in range(tx.outputs_count):
|
for o in range(tx.outputs_count):
|
||||||
progress.advance()
|
progress.advance()
|
||||||
# STAGE_REQUEST_5_OUTPUT
|
# STAGE_REQUEST_5_OUTPUT
|
||||||
@ -396,7 +509,11 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
key_sign = node_derive(root, txi.address_n)
|
key_sign = node_derive(root, txi.address_n)
|
||||||
key_sign_pub = key_sign.public_key()
|
key_sign_pub = key_sign.public_key()
|
||||||
hash143_hash = hash143.preimage_hash(
|
hash143_hash = hash143.preimage_hash(
|
||||||
coin, tx, txi, ecdsa_hash_pubkey(key_sign_pub), get_hash_type(coin)
|
coin,
|
||||||
|
tx,
|
||||||
|
txi,
|
||||||
|
ecdsa_hash_pubkey(key_sign_pub, coin),
|
||||||
|
get_hash_type(coin),
|
||||||
)
|
)
|
||||||
|
|
||||||
signature = ecdsa_sign(key_sign, hash143_hash)
|
signature = ecdsa_sign(key_sign, hash143_hash)
|
||||||
@ -420,6 +537,7 @@ async def sign_tx(tx: SignTx, root: bip32.HDNode):
|
|||||||
tx_req.serialized = tx_ser
|
tx_req.serialized = tx_ser
|
||||||
|
|
||||||
write_uint32(tx_ser.serialized_tx, tx.lock_time)
|
write_uint32(tx_ser.serialized_tx, tx.lock_time)
|
||||||
|
|
||||||
if tx.overwintered:
|
if tx.overwintered:
|
||||||
write_uint32(tx_ser.serialized_tx, tx.expiry) # expiryHeight
|
write_uint32(tx_ser.serialized_tx, tx.expiry) # expiryHeight
|
||||||
write_varint(tx_ser.serialized_tx, 0) # nJoinSplit
|
write_varint(tx_ser.serialized_tx, 0) # nJoinSplit
|
||||||
@ -435,11 +553,16 @@ async def get_prevtx_output_value(
|
|||||||
# STAGE_REQUEST_2_PREV_META
|
# STAGE_REQUEST_2_PREV_META
|
||||||
tx = await request_tx_meta(tx_req, prev_hash)
|
tx = await request_tx_meta(tx_req, prev_hash)
|
||||||
|
|
||||||
|
if coin.decred:
|
||||||
|
txh = HashWriter(blake256)
|
||||||
|
else:
|
||||||
txh = HashWriter(sha256)
|
txh = HashWriter(sha256)
|
||||||
|
|
||||||
if tx.overwintered:
|
if tx.overwintered:
|
||||||
write_uint32(txh, tx.version | OVERWINTERED) # nVersion | fOverwintered
|
write_uint32(txh, tx.version | OVERWINTERED) # nVersion | fOverwintered
|
||||||
write_uint32(txh, coin.version_group_id) # nVersionGroupId
|
write_uint32(txh, coin.version_group_id) # nVersionGroupId
|
||||||
|
elif coin.decred:
|
||||||
|
write_uint32(txh, tx.version | DECRED_SERIALIZE_NO_WITNESS)
|
||||||
else:
|
else:
|
||||||
write_uint32(txh, tx.version) # nVersion
|
write_uint32(txh, tx.version) # nVersion
|
||||||
|
|
||||||
@ -448,6 +571,9 @@ async def get_prevtx_output_value(
|
|||||||
for i in range(tx.inputs_cnt):
|
for i in range(tx.inputs_cnt):
|
||||||
# STAGE_REQUEST_2_PREV_INPUT
|
# STAGE_REQUEST_2_PREV_INPUT
|
||||||
txi = await request_tx_input(tx_req, i, prev_hash)
|
txi = await request_tx_input(tx_req, i, prev_hash)
|
||||||
|
if coin.decred:
|
||||||
|
write_tx_input_decred(txh, txi)
|
||||||
|
else:
|
||||||
write_tx_input(txh, txi)
|
write_tx_input(txh, txi)
|
||||||
|
|
||||||
write_varint(txh, tx.outputs_cnt)
|
write_varint(txh, tx.outputs_cnt)
|
||||||
@ -458,10 +584,19 @@ async def get_prevtx_output_value(
|
|||||||
write_tx_output(txh, txo_bin)
|
write_tx_output(txh, txo_bin)
|
||||||
if o == prev_index:
|
if o == prev_index:
|
||||||
total_out += txo_bin.amount
|
total_out += txo_bin.amount
|
||||||
|
if (
|
||||||
|
coin.decred
|
||||||
|
and txo_bin.decred_script_version is not None
|
||||||
|
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)
|
write_uint32(txh, tx.lock_time)
|
||||||
|
|
||||||
if tx.overwintered:
|
if tx.overwintered or coin.decred:
|
||||||
write_uint32(txh, tx.expiry)
|
write_uint32(txh, tx.expiry)
|
||||||
|
|
||||||
ofs = 0
|
ofs = 0
|
||||||
@ -626,7 +761,7 @@ def input_derive_script(
|
|||||||
return input_script_p2wsh_in_p2sh(witness_script_hash)
|
return input_script_p2wsh_in_p2sh(witness_script_hash)
|
||||||
|
|
||||||
# p2wpkh in p2sh
|
# p2wpkh in p2sh
|
||||||
return input_script_p2wpkh_in_p2sh(ecdsa_hash_pubkey(pubkey))
|
return input_script_p2wpkh_in_p2sh(ecdsa_hash_pubkey(pubkey, coin))
|
||||||
|
|
||||||
elif i.script_type == InputScriptType.SPENDWITNESS:
|
elif i.script_type == InputScriptType.SPENDWITNESS:
|
||||||
# native p2wpkh or p2wsh
|
# native p2wpkh or p2wsh
|
||||||
@ -636,7 +771,7 @@ def input_derive_script(
|
|||||||
# p2sh multisig
|
# p2sh multisig
|
||||||
signature_index = multisig_pubkey_index(i.multisig, pubkey)
|
signature_index = multisig_pubkey_index(i.multisig, pubkey)
|
||||||
return input_script_multisig(
|
return input_script_multisig(
|
||||||
i.multisig, signature, signature_index, get_hash_type(coin)
|
i.multisig, signature, signature_index, get_hash_type(coin), coin
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -5,10 +5,13 @@ from trezor.messages.TxOutputBinType import TxOutputBinType
|
|||||||
from apps.common.writers import (
|
from apps.common.writers import (
|
||||||
write_bytes,
|
write_bytes,
|
||||||
write_bytes_reversed,
|
write_bytes_reversed,
|
||||||
|
write_uint8,
|
||||||
|
write_uint16_le,
|
||||||
write_uint32_le,
|
write_uint32_le,
|
||||||
write_uint64_le,
|
write_uint64_le,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
write_uint16 = write_uint16_le
|
||||||
write_uint32 = write_uint32_le
|
write_uint32 = write_uint32_le
|
||||||
write_uint64 = write_uint64_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)
|
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 or 0)
|
||||||
|
write_uint8(w, i.decred_tree or 0)
|
||||||
|
write_uint32(w, i.sequence)
|
||||||
|
|
||||||
|
|
||||||
|
def write_tx_input_decred_witness(w, i: TxInputType):
|
||||||
|
write_uint64(w, i.amount or 0)
|
||||||
|
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):
|
def write_tx_output(w, o: TxOutputBinType):
|
||||||
write_uint64(w, o.amount)
|
write_uint64(w, o.amount)
|
||||||
|
if o.decred_script_version is not None:
|
||||||
|
write_uint16(w, o.decred_script_version)
|
||||||
write_varint(w, len(o.script_pubkey))
|
write_varint(w, len(o.script_pubkey))
|
||||||
write_bytes(w, o.script_pubkey)
|
write_bytes(w, o.script_pubkey)
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ async def verify_message(ctx, msg):
|
|||||||
elif script_type == SPENDP2SHWITNESS:
|
elif script_type == SPENDP2SHWITNESS:
|
||||||
addr = address_p2wpkh_in_p2sh(pubkey, coin)
|
addr = address_p2wpkh_in_p2sh(pubkey, coin)
|
||||||
elif script_type == SPENDWITNESS:
|
elif script_type == SPENDWITNESS:
|
||||||
addr = address_p2wpkh(pubkey, coin.bech32_prefix)
|
addr = address_p2wpkh(pubkey, coin)
|
||||||
else:
|
else:
|
||||||
raise wire.ProcessError("Invalid signature")
|
raise wire.ProcessError("Invalid signature")
|
||||||
|
|
||||||
|
@ -71,6 +71,12 @@ def groestl512d_32(data: bytes) -> bytes:
|
|||||||
return groestl512(groestl512(data).digest()).digest()[:4]
|
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:
|
def encode_check(data: bytes, digestfunc=sha256d_32) -> str:
|
||||||
"""
|
"""
|
||||||
Convert bytes to base58 encoded string, append checksum.
|
Convert bytes to base58 encoded string, append checksum.
|
||||||
|
@ -41,7 +41,7 @@ class TestAddress(unittest.TestCase):
|
|||||||
coin = coins.by_name('Testnet')
|
coin = coins.by_name('Testnet')
|
||||||
address = address_p2wpkh(
|
address = address_p2wpkh(
|
||||||
unhexlify('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
|
unhexlify('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
|
||||||
coin.bech32_prefix
|
coin
|
||||||
)
|
)
|
||||||
self.assertEqual(address, 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx')
|
self.assertEqual(address, 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx')
|
||||||
|
|
||||||
|
@ -60,15 +60,15 @@ class TestAddressGRS(unittest.TestCase):
|
|||||||
root = bip32.from_seed(seed, coin.curve_name)
|
root = bip32.from_seed(seed, coin.curve_name)
|
||||||
|
|
||||||
node = node_derive(root, [84 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 1, 0])
|
node = node_derive(root, [84 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 1, 0])
|
||||||
address = address_p2wpkh(node.public_key(), coin.bech32_prefix)
|
address = address_p2wpkh(node.public_key(), coin)
|
||||||
self.assertEqual(address, 'grs1qzfpwn55tvkxcw0xwfa0g8k2gtlzlgkcq3z000e')
|
self.assertEqual(address, 'grs1qzfpwn55tvkxcw0xwfa0g8k2gtlzlgkcq3z000e')
|
||||||
|
|
||||||
node = node_derive(root, [84 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 1, 1])
|
node = node_derive(root, [84 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 1, 1])
|
||||||
address = address_p2wpkh(node.public_key(), coin.bech32_prefix)
|
address = address_p2wpkh(node.public_key(), coin)
|
||||||
self.assertEqual(address, 'grs1qxsgwl66tx7tsuwfm4kk5c5dh6tlfpr4qjqg6gg')
|
self.assertEqual(address, 'grs1qxsgwl66tx7tsuwfm4kk5c5dh6tlfpr4qjqg6gg')
|
||||||
|
|
||||||
node = node_derive(root, [84 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 0, 0])
|
node = node_derive(root, [84 | 0x80000000, 17 | 0x80000000, 0 | 0x80000000, 0, 0])
|
||||||
address = address_p2wpkh(node.public_key(), coin.bech32_prefix)
|
address = address_p2wpkh(node.public_key(), coin)
|
||||||
self.assertEqual(address, 'grs1qw4teyraux2s77nhjdwh9ar8rl9dt7zww8r6lne')
|
self.assertEqual(address, 'grs1qw4teyraux2s77nhjdwh9ar8rl9dt7zww8r6lne')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user