mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-22 22:38:08 +00:00
core/sign_tx: Move non-Bitcoin functionality to Bitcoinlike class.
This commit is contained in:
parent
613c81ea66
commit
c190eed7fc
@ -16,16 +16,20 @@ from apps.wallet.sign_tx import (
|
||||
)
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
from apps.wallet.sign_tx import decred
|
||||
from apps.wallet.sign_tx import decred, bitcoinlike
|
||||
|
||||
|
||||
async def sign_tx(ctx, msg, keychain):
|
||||
coin_name = msg.coin_name if msg.coin_name is not None else "Bitcoin"
|
||||
coin = coins.by_name(coin_name)
|
||||
if not utils.BITCOIN_ONLY and coin.decred:
|
||||
signer = decred.Decred().signer(msg, keychain, coin)
|
||||
coinsig = decred.Decred()
|
||||
elif not utils.BITCOIN_ONLY and coin_name not in ("Bitcoin", "Regtest", "Testnet"):
|
||||
coinsig = bitcoinlike.Bitcoinlike()
|
||||
else:
|
||||
signer = signing.Bitcoin().signer(msg, keychain, coin)
|
||||
coinsig = signing.Bitcoin()
|
||||
|
||||
signer = coinsig.signer(msg, keychain, coin)
|
||||
|
||||
res = None
|
||||
while True:
|
||||
|
183
core/src/apps/wallet/sign_tx/bitcoinlike.py
Normal file
183
core/src/apps/wallet/sign_tx/bitcoinlike.py
Normal file
@ -0,0 +1,183 @@
|
||||
import gc
|
||||
from micropython import const
|
||||
|
||||
from trezor.crypto import cashaddr
|
||||
from trezor.messages import FailureType, InputScriptType
|
||||
from trezor.messages.SignTx import SignTx
|
||||
from trezor.messages.TransactionType import TransactionType
|
||||
from trezor.messages.TxInputType import TxInputType
|
||||
from trezor.messages.TxOutputType import TxOutputType
|
||||
from trezor.messages.TxRequestSerializedType import TxRequestSerializedType
|
||||
|
||||
from apps.wallet.sign_tx import addresses, helpers, multisig, signing, writers, zcash
|
||||
|
||||
if False:
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Bitcoinlike(signing.Bitcoin):
|
||||
def init_hash143(self) -> None:
|
||||
if self.coin.overwintered:
|
||||
if self.tx.version == 3:
|
||||
branch_id = self.tx.branch_id or 0x5BA81B19 # Overwinter
|
||||
self.hash143 = zcash.Zip143(branch_id) # ZIP-0143 transaction hashing
|
||||
elif self.tx.version == 4:
|
||||
branch_id = self.tx.branch_id or 0x76B809BB # Sapling
|
||||
self.hash143 = zcash.Zip243(branch_id) # ZIP-0243 transaction hashing
|
||||
else:
|
||||
raise signing.SigningError(
|
||||
FailureType.DataError,
|
||||
"Unsupported version for overwintered transaction",
|
||||
)
|
||||
else:
|
||||
super().init_hash143()
|
||||
|
||||
async def phase1_process_segwit_input(self, i: int, txi: TxInputType) -> None:
|
||||
if not self.coin.segwit:
|
||||
raise signing.SigningError(
|
||||
FailureType.DataError, "Segwit not enabled on this coin"
|
||||
)
|
||||
await super().phase1_process_segwit_input(i, txi)
|
||||
|
||||
async def phase1_process_nonsegwit_input(self, i: int, txi: TxInputType) -> None:
|
||||
if self.coin.force_bip143 or self.coin.overwintered:
|
||||
await self.phase1_process_bip143_input(i, txi)
|
||||
else:
|
||||
await super().phase1_process_nonsegwit_input(i, txi)
|
||||
|
||||
async def phase1_process_bip143_input(self, i: int, txi: TxInputType) -> None:
|
||||
if not txi.amount:
|
||||
raise signing.SigningError(
|
||||
FailureType.DataError, "Expected input with amount"
|
||||
)
|
||||
self.segwit[i] = False
|
||||
self.bip143_in += txi.amount
|
||||
self.total_in += txi.amount
|
||||
|
||||
async def phase2_sign_nonsegwit_input(self, i_sign: int) -> None:
|
||||
if self.coin.force_bip143 or self.coin.overwintered:
|
||||
await self.phase2_sign_bip143_input(i_sign)
|
||||
else:
|
||||
await super().phase2_sign_nonsegwit_input(i_sign)
|
||||
|
||||
async def phase2_sign_bip143_input(self, i_sign) -> None:
|
||||
# STAGE_REQUEST_SEGWIT_INPUT
|
||||
txi_sign = await helpers.request_tx_input(self.tx_req, i_sign, self.coin)
|
||||
self.input_check_wallet_path(txi_sign)
|
||||
self.input_check_multisig_fingerprint(txi_sign)
|
||||
|
||||
is_bip143 = (
|
||||
txi_sign.script_type == InputScriptType.SPENDADDRESS
|
||||
or txi_sign.script_type == InputScriptType.SPENDMULTISIG
|
||||
)
|
||||
if not is_bip143 or txi_sign.amount > self.bip143_in:
|
||||
raise signing.SigningError(
|
||||
FailureType.ProcessError, "Transaction has changed during signing"
|
||||
)
|
||||
self.bip143_in -= txi_sign.amount
|
||||
|
||||
key_sign = self.keychain.derive(txi_sign.address_n, self.coin.curve_name)
|
||||
key_sign_pub = key_sign.public_key()
|
||||
self.hash143_hash = self.hash143.preimage_hash(
|
||||
self.coin,
|
||||
self.tx,
|
||||
txi_sign,
|
||||
addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin),
|
||||
self.get_hash_type(),
|
||||
)
|
||||
|
||||
# if multisig, check if signing with a key that is included in multisig
|
||||
if txi_sign.multisig:
|
||||
multisig.multisig_pubkey_index(txi_sign.multisig, key_sign_pub)
|
||||
|
||||
signature = signing.ecdsa_sign(key_sign, self.hash143_hash)
|
||||
|
||||
# serialize input with correct signature
|
||||
gc.collect()
|
||||
txi_sign.script_sig = self.input_derive_script(
|
||||
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
|
||||
self.write_sign_tx_header(w_txi_sign, True in self.segwit.values())
|
||||
writers.write_tx_input(w_txi_sign, txi_sign)
|
||||
self.tx_req.serialized = TxRequestSerializedType(i_sign, signature, w_txi_sign)
|
||||
|
||||
def on_negative_fee(self) -> None:
|
||||
# some coins require negative fees for reward TX
|
||||
if not self.coin.negative_fee:
|
||||
super().on_negative_fee()
|
||||
|
||||
def get_raw_address(self, o: TxOutputType) -> bytes:
|
||||
if self.coin.cashaddr_prefix is not None and o.address.startswith(
|
||||
self.coin.cashaddr_prefix + ":"
|
||||
):
|
||||
prefix, addr = o.address.split(":")
|
||||
version, data = cashaddr.decode(prefix, addr)
|
||||
if version == cashaddr.ADDRESS_TYPE_P2KH:
|
||||
version = self.coin.address_type
|
||||
elif version == cashaddr.ADDRESS_TYPE_P2SH:
|
||||
version = self.coin.address_type_p2sh
|
||||
else:
|
||||
raise signing.SigningError("Unknown cashaddr address type")
|
||||
return bytes([version]) + data
|
||||
else:
|
||||
return super().get_raw_address(o)
|
||||
|
||||
def get_hash_type(self) -> int:
|
||||
SIGHASH_FORKID = const(0x40)
|
||||
hashtype = super().get_hash_type()
|
||||
if self.coin.fork_id is not None:
|
||||
hashtype |= (self.coin.fork_id << 8) | SIGHASH_FORKID
|
||||
return hashtype
|
||||
|
||||
def write_sign_tx_footer(self, w: writers.Writer) -> None:
|
||||
super().write_sign_tx_footer(w)
|
||||
|
||||
if self.coin.overwintered:
|
||||
if self.tx.version == 3:
|
||||
writers.write_uint32(w, self.tx.expiry) # expiryHeight
|
||||
writers.write_varint(w, 0) # nJoinSplit
|
||||
elif self.tx.version == 4:
|
||||
writers.write_uint32(w, self.tx.expiry) # expiryHeight
|
||||
writers.write_uint64(w, 0) # valueBalance
|
||||
writers.write_varint(w, 0) # nShieldedSpend
|
||||
writers.write_varint(w, 0) # nShieldedOutput
|
||||
writers.write_varint(w, 0) # nJoinSplit
|
||||
else:
|
||||
raise signing.SigningError(
|
||||
FailureType.DataError,
|
||||
"Unsupported version for overwintered transaction",
|
||||
)
|
||||
|
||||
def write_tx_header(
|
||||
self, w: writers.Writer, tx: Union[SignTx, TransactionType], has_segwit: bool
|
||||
) -> None:
|
||||
if self.coin.overwintered:
|
||||
# nVersion | fOverwintered
|
||||
writers.write_uint32(w, tx.version | zcash.OVERWINTERED)
|
||||
writers.write_uint32(w, tx.version_group_id) # nVersionGroupId
|
||||
else:
|
||||
writers.write_uint32(w, tx.version) # nVersion
|
||||
if self.coin.timestamp:
|
||||
writers.write_uint32(w, tx.timestamp)
|
||||
if has_segwit:
|
||||
writers.write_varint(w, 0x00) # segwit witness marker
|
||||
writers.write_varint(w, 0x01) # segwit witness flag
|
||||
|
||||
async def write_prev_tx_footer(
|
||||
self, w: writers.Writer, tx: TransactionType, prev_hash: bytes
|
||||
) -> None:
|
||||
await super().write_prev_tx_footer(w, tx, prev_hash)
|
||||
|
||||
if self.coin.extra_data:
|
||||
ofs = 0
|
||||
while ofs < tx.extra_data_len:
|
||||
size = min(1024, tx.extra_data_len - ofs)
|
||||
data = await helpers.request_tx_extra_data(
|
||||
self.tx_req, ofs, size, prev_hash
|
||||
)
|
||||
writers.write_bytes_unchecked(w, data)
|
||||
ofs += len(data)
|
@ -12,11 +12,7 @@ from trezor.utils import HashWriter
|
||||
|
||||
from apps.common import coininfo, seed
|
||||
from apps.wallet.sign_tx import addresses, helpers, multisig, progress, scripts, writers
|
||||
from apps.wallet.sign_tx.signing import (
|
||||
Bitcoin,
|
||||
SigningError,
|
||||
ecdsa_sign,
|
||||
)
|
||||
from apps.wallet.sign_tx.signing import Bitcoin, SigningError, ecdsa_sign
|
||||
|
||||
DECRED_SERIALIZE_FULL = const(0 << 16)
|
||||
DECRED_SERIALIZE_NO_WITNESS = const(1 << 16)
|
||||
@ -76,7 +72,7 @@ class Decred(Bitcoin):
|
||||
await super().phase1_process_input(i, txi)
|
||||
w_txi = writers.empty_bytearray(8 if i == 0 else 0 + 9 + len(txi.prev_hash))
|
||||
if i == 0: # serializing first input => prepend headers
|
||||
self.write_tx_header(w_txi)
|
||||
self.write_sign_tx_header(w_txi, False)
|
||||
writers.write_tx_input_decred(w_txi, txi)
|
||||
self.tx_req.serialized = TxRequestSerializedType(None, None, w_txi)
|
||||
|
||||
|
@ -2,11 +2,12 @@ import gc
|
||||
from micropython import const
|
||||
|
||||
from trezor import utils
|
||||
from trezor.crypto import base58, bip32, cashaddr, der
|
||||
from trezor.crypto import base58, bip32, der
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.crypto.hashlib import sha256
|
||||
from trezor.messages import FailureType, InputScriptType, OutputScriptType
|
||||
from trezor.messages.SignTx import SignTx
|
||||
from trezor.messages.TransactionType import TransactionType
|
||||
from trezor.messages.TxInputType import TxInputType
|
||||
from trezor.messages.TxOutputBinType import TxOutputBinType
|
||||
from trezor.messages.TxOutputType import TxOutputType
|
||||
@ -26,11 +27,8 @@ from apps.wallet.sign_tx import (
|
||||
writers,
|
||||
)
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
from apps.wallet.sign_tx import zcash
|
||||
|
||||
if False:
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
# the number of bip32 levels used in a wallet (chain and address)
|
||||
_BIP32_WALLET_DEPTH = const(2)
|
||||
@ -73,7 +71,9 @@ class Bitcoin:
|
||||
# - check that nothing changed
|
||||
await self.phase2()
|
||||
|
||||
def initialize(self, tx: SignTx, keychain: seed.Keychain, coin: coininfo.CoinInfo) -> None:
|
||||
def initialize(
|
||||
self, tx: SignTx, keychain: seed.Keychain, coin: coininfo.CoinInfo
|
||||
) -> None:
|
||||
self.coin = coin
|
||||
self.tx = helpers.sanitize_sign_tx(tx, self.coin)
|
||||
self.keychain = keychain
|
||||
@ -81,9 +81,13 @@ class Bitcoin:
|
||||
self.multisig_fp = (
|
||||
multisig.MultisigFingerprint()
|
||||
) # control checksum of multisig inputs
|
||||
self.wallet_path = [] # type: Optional[List[int]] # common prefix of input paths
|
||||
self.wallet_path = (
|
||||
[]
|
||||
) # type: Optional[List[int]] # common prefix of input paths
|
||||
self.bip143_in = 0 # sum of segwit input amounts
|
||||
self.segwit = {} # type: Dict[int, bool] # dict of booleans stating if input is segwit
|
||||
self.segwit = (
|
||||
{}
|
||||
) # type: Dict[int, bool] # dict of booleans stating if input is segwit
|
||||
self.total_in = 0 # sum of input amounts
|
||||
self.total_out = 0 # sum of output amounts
|
||||
self.change_out = 0 # change output amount
|
||||
@ -99,20 +103,7 @@ class Bitcoin:
|
||||
self.init_hash143()
|
||||
|
||||
def init_hash143(self) -> None:
|
||||
if not utils.BITCOIN_ONLY and self.coin.overwintered:
|
||||
if self.tx.version == 3:
|
||||
branch_id = self.tx.branch_id or 0x5BA81B19 # Overwinter
|
||||
self.hash143 = zcash.Zip143(branch_id) # ZIP-0143 transaction hashing
|
||||
elif self.tx.version == 4:
|
||||
branch_id = self.tx.branch_id or 0x76B809BB # Sapling
|
||||
self.hash143 = zcash.Zip243(branch_id) # ZIP-0243 transaction hashing
|
||||
else:
|
||||
raise SigningError(
|
||||
FailureType.DataError,
|
||||
"Unsupported version for overwintered transaction",
|
||||
)
|
||||
else:
|
||||
self.hash143 = segwit_bip143.Bip143() # BIP-0143 transaction hashing
|
||||
self.hash143 = segwit_bip143.Bip143() # BIP-0143 transaction hashing
|
||||
|
||||
async def phase1(self) -> None:
|
||||
weight = tx_weight.TxWeightCalculator(
|
||||
@ -174,37 +165,28 @@ class Bitcoin:
|
||||
InputScriptType.SPENDWITNESS,
|
||||
InputScriptType.SPENDP2SHWITNESS,
|
||||
):
|
||||
if not self.coin.segwit:
|
||||
raise SigningError(
|
||||
FailureType.DataError, "Segwit not enabled on this coin"
|
||||
)
|
||||
if not txi.amount:
|
||||
raise SigningError(FailureType.DataError, "Segwit input without amount")
|
||||
self.segwit[i] = True
|
||||
self.bip143_in += txi.amount
|
||||
self.total_in += txi.amount
|
||||
await self.phase1_process_segwit_input(i, txi)
|
||||
elif txi.script_type in (
|
||||
InputScriptType.SPENDADDRESS,
|
||||
InputScriptType.SPENDMULTISIG,
|
||||
):
|
||||
if not utils.BITCOIN_ONLY and (
|
||||
self.coin.force_bip143 or self.coin.overwintered
|
||||
):
|
||||
if not txi.amount:
|
||||
raise SigningError(
|
||||
FailureType.DataError, "Expected input with amount"
|
||||
)
|
||||
self.segwit[i] = False
|
||||
self.bip143_in += txi.amount
|
||||
self.total_in += txi.amount
|
||||
else:
|
||||
self.segwit[i] = False
|
||||
self.total_in += await self.get_prevtx_output_value(
|
||||
txi.prev_hash, txi.prev_index
|
||||
)
|
||||
await self.phase1_process_nonsegwit_input(i, txi)
|
||||
else:
|
||||
raise SigningError(FailureType.DataError, "Wrong input script type")
|
||||
|
||||
async def phase1_process_segwit_input(self, i: int, txi: TxInputType) -> None:
|
||||
if not txi.amount:
|
||||
raise SigningError(FailureType.DataError, "Segwit input without amount")
|
||||
self.segwit[i] = True
|
||||
self.bip143_in += txi.amount
|
||||
self.total_in += txi.amount
|
||||
|
||||
async def phase1_process_nonsegwit_input(self, i: int, txi: TxInputType) -> None:
|
||||
self.segwit[i] = False
|
||||
self.total_in += await self.get_prevtx_output_value(
|
||||
txi.prev_hash, txi.prev_index
|
||||
)
|
||||
|
||||
async def phase1_confirm_output(
|
||||
self, i: int, txo: TxOutputType, txo_bin: TxOutputBinType
|
||||
) -> None:
|
||||
@ -218,10 +200,8 @@ class Bitcoin:
|
||||
self.hash143.add_output(txo_bin)
|
||||
self.total_out += txo_bin.amount
|
||||
|
||||
def on_negative_fee(self):
|
||||
# some coins require negative fees for reward TX
|
||||
if utils.BITCOIN_ONLY or not self.coin.negative_fee:
|
||||
raise SigningError(FailureType.NotEnoughFunds, "Not enough funds")
|
||||
def on_negative_fee(self) -> None:
|
||||
raise SigningError(FailureType.NotEnoughFunds, "Not enough funds")
|
||||
|
||||
async def phase2(self) -> None:
|
||||
self.tx_req.serialized = None
|
||||
@ -231,12 +211,8 @@ class Bitcoin:
|
||||
progress.advance()
|
||||
if self.segwit[i]:
|
||||
await self.phase2_serialize_segwit_input(i)
|
||||
elif not utils.BITCOIN_ONLY and (
|
||||
self.coin.force_bip143 or self.coin.overwintered
|
||||
):
|
||||
await self.phase2_sign_bip143_input(i)
|
||||
else:
|
||||
await self.phase2_sign_legacy_input(i)
|
||||
await self.phase2_sign_nonsegwit_input(i)
|
||||
|
||||
# Serialize outputs.
|
||||
tx_ser = TxRequestSerializedType()
|
||||
@ -257,6 +233,7 @@ class Bitcoin:
|
||||
tx_ser.signature_index = i
|
||||
tx_ser.signature = signature
|
||||
elif any_segwit:
|
||||
# TODO what if a non-segwit input follows after a segwit input?
|
||||
tx_ser.serialized_tx += bytearray(
|
||||
1
|
||||
) # empty witness for non-segwit inputs
|
||||
@ -265,27 +242,7 @@ class Bitcoin:
|
||||
|
||||
self.tx_req.serialized = tx_ser
|
||||
|
||||
writers.write_uint32(tx_ser.serialized_tx, self.tx.lock_time)
|
||||
|
||||
if not utils.BITCOIN_ONLY and self.coin.overwintered:
|
||||
if self.tx.version == 3:
|
||||
writers.write_uint32(
|
||||
tx_ser.serialized_tx, self.tx.expiry
|
||||
) # expiryHeight
|
||||
writers.write_varint(tx_ser.serialized_tx, 0) # nJoinSplit
|
||||
elif self.tx.version == 4:
|
||||
writers.write_uint32(
|
||||
tx_ser.serialized_tx, self.tx.expiry
|
||||
) # expiryHeight
|
||||
writers.write_uint64(tx_ser.serialized_tx, 0) # valueBalance
|
||||
writers.write_varint(tx_ser.serialized_tx, 0) # nShieldedSpend
|
||||
writers.write_varint(tx_ser.serialized_tx, 0) # nShieldedOutput
|
||||
writers.write_varint(tx_ser.serialized_tx, 0) # nJoinSplit
|
||||
else:
|
||||
raise SigningError(
|
||||
FailureType.DataError,
|
||||
"Unsupported version for overwintered transaction",
|
||||
)
|
||||
self.write_sign_tx_footer(tx_ser.serialized_tx)
|
||||
|
||||
await helpers.request_tx_finish(self.tx_req)
|
||||
|
||||
@ -309,7 +266,7 @@ class Bitcoin:
|
||||
7 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4
|
||||
)
|
||||
if i_sign == 0: # serializing first input => prepend headers
|
||||
self.write_tx_header(w_txi)
|
||||
self.write_sign_tx_header(w_txi, True)
|
||||
writers.write_tx_input(w_txi, txi_sign)
|
||||
self.tx_req.serialized = TxRequestSerializedType(serialized_tx=w_txi)
|
||||
|
||||
@ -349,62 +306,13 @@ class Bitcoin:
|
||||
|
||||
return witness, signature
|
||||
|
||||
async def phase2_sign_bip143_input(self, i_sign) -> None:
|
||||
# STAGE_REQUEST_SEGWIT_INPUT
|
||||
txi_sign = await helpers.request_tx_input(self.tx_req, i_sign, self.coin)
|
||||
self.input_check_wallet_path(txi_sign)
|
||||
self.input_check_multisig_fingerprint(txi_sign)
|
||||
|
||||
is_bip143 = (
|
||||
txi_sign.script_type == InputScriptType.SPENDADDRESS
|
||||
or txi_sign.script_type == InputScriptType.SPENDMULTISIG
|
||||
)
|
||||
if not is_bip143 or txi_sign.amount > self.bip143_in:
|
||||
raise SigningError(
|
||||
FailureType.ProcessError, "Transaction has changed during signing"
|
||||
)
|
||||
self.bip143_in -= txi_sign.amount
|
||||
|
||||
key_sign = self.keychain.derive(txi_sign.address_n, self.coin.curve_name)
|
||||
key_sign_pub = key_sign.public_key()
|
||||
self.hash143_hash = self.hash143.preimage_hash(
|
||||
self.coin,
|
||||
self.tx,
|
||||
txi_sign,
|
||||
addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin),
|
||||
self.get_hash_type(),
|
||||
)
|
||||
|
||||
# if multisig, check if signing with a key that is included in multisig
|
||||
if txi_sign.multisig:
|
||||
multisig.multisig_pubkey_index(txi_sign.multisig, key_sign_pub)
|
||||
|
||||
signature = ecdsa_sign(key_sign, self.hash143_hash)
|
||||
|
||||
# serialize input with correct signature
|
||||
gc.collect()
|
||||
txi_sign.script_sig = self.input_derive_script(
|
||||
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
|
||||
self.write_tx_header(w_txi_sign)
|
||||
writers.write_tx_input(w_txi_sign, txi_sign)
|
||||
self.tx_req.serialized = TxRequestSerializedType(i_sign, signature, w_txi_sign)
|
||||
|
||||
async def phase2_sign_legacy_input(self, i_sign) -> None:
|
||||
async def phase2_sign_nonsegwit_input(self, i_sign: int) -> None:
|
||||
# hash of what we are signing with this input
|
||||
h_sign = utils.HashWriter(sha256())
|
||||
# same as h_first, checked before signing the digest
|
||||
h_second = utils.HashWriter(sha256())
|
||||
|
||||
writers.write_uint32(h_sign, self.tx.version) # nVersion
|
||||
if not utils.BITCOIN_ONLY and self.coin.timestamp:
|
||||
writers.write_uint32(h_sign, self.tx.timestamp)
|
||||
|
||||
writers.write_varint(h_sign, self.tx.inputs_count)
|
||||
self.write_sign_tx_header(h_sign, has_segwit=False)
|
||||
|
||||
for i in range(self.tx.inputs_count):
|
||||
# STAGE_REQUEST_4_INPUT
|
||||
@ -473,7 +381,7 @@ class Bitcoin:
|
||||
5 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4
|
||||
)
|
||||
if i_sign == 0: # serializing first input => prepend headers
|
||||
self.write_tx_header(w_txi_sign)
|
||||
self.write_sign_tx_header(w_txi_sign, True in self.segwit.values())
|
||||
writers.write_tx_input(w_txi_sign, txi_sign)
|
||||
self.tx_req.serialized = TxRequestSerializedType(i_sign, signature, w_txi_sign)
|
||||
|
||||
@ -492,7 +400,7 @@ class Bitcoin:
|
||||
return w_txo_bin
|
||||
|
||||
async def get_prevtx_output_value(self, prev_hash: bytes, prev_index: int) -> int:
|
||||
total_out = 0 # sum of output amounts
|
||||
amount_out = 0 # sum of output amounts
|
||||
|
||||
# STAGE_REQUEST_2_PREV_META
|
||||
tx = await helpers.request_tx_meta(self.tx_req, self.coin, prev_hash)
|
||||
@ -504,16 +412,8 @@ class Bitcoin:
|
||||
|
||||
txh = utils.HashWriter(sha256())
|
||||
|
||||
if not utils.BITCOIN_ONLY and self.coin.overwintered:
|
||||
writers.write_uint32(
|
||||
txh, tx.version | zcash.OVERWINTERED
|
||||
) # nVersion | fOverwintered
|
||||
writers.write_uint32(txh, tx.version_group_id) # nVersionGroupId
|
||||
else:
|
||||
writers.write_uint32(txh, tx.version) # nVersion
|
||||
if not utils.BITCOIN_ONLY and self.coin.timestamp:
|
||||
writers.write_uint32(txh, tx.timestamp)
|
||||
|
||||
# TODO set has_segwit correctly
|
||||
self.write_tx_header(txh, tx, has_segwit=False)
|
||||
writers.write_varint(txh, tx.inputs_cnt)
|
||||
|
||||
for i in range(tx.inputs_cnt):
|
||||
@ -530,19 +430,9 @@ class Bitcoin:
|
||||
)
|
||||
writers.write_tx_output(txh, txo_bin)
|
||||
if o == prev_index:
|
||||
total_out += txo_bin.amount
|
||||
amount_out = txo_bin.amount
|
||||
|
||||
writers.write_uint32(txh, tx.lock_time)
|
||||
|
||||
if not utils.BITCOIN_ONLY and self.coin.extra_data:
|
||||
ofs = 0
|
||||
while ofs < tx.extra_data_len:
|
||||
size = min(1024, tx.extra_data_len - ofs)
|
||||
data = await helpers.request_tx_extra_data(
|
||||
self.tx_req, ofs, size, prev_hash
|
||||
)
|
||||
writers.write_bytes_unchecked(txh, data)
|
||||
ofs += len(data)
|
||||
await self.write_prev_tx_footer(txh, tx, prev_hash)
|
||||
|
||||
if (
|
||||
writers.get_tx_hash(txh, double=self.coin.sign_hash_double, reverse=True)
|
||||
@ -552,32 +442,34 @@ class Bitcoin:
|
||||
FailureType.ProcessError, "Encountered invalid prev_hash"
|
||||
)
|
||||
|
||||
return total_out
|
||||
return amount_out
|
||||
|
||||
# TX Helpers
|
||||
# ===
|
||||
|
||||
def get_hash_type(self) -> int:
|
||||
SIGHASH_FORKID = const(0x40)
|
||||
SIGHASH_ALL = const(0x01)
|
||||
hashtype = SIGHASH_ALL
|
||||
if self.coin.fork_id is not None:
|
||||
hashtype |= (self.coin.fork_id << 8) | SIGHASH_FORKID
|
||||
return hashtype
|
||||
return SIGHASH_ALL
|
||||
|
||||
def write_tx_header(self, w: writers.Writer) -> None:
|
||||
if not utils.BITCOIN_ONLY and self.coin.overwintered:
|
||||
# nVersion | fOverwintered
|
||||
writers.write_uint32(w, self.tx.version | zcash.OVERWINTERED)
|
||||
writers.write_uint32(w, self.tx.version_group_id) # nVersionGroupId
|
||||
else:
|
||||
writers.write_uint32(w, self.tx.version) # nVersion
|
||||
if not utils.BITCOIN_ONLY and self.coin.timestamp:
|
||||
writers.write_uint32(w, self.tx.timestamp)
|
||||
if True in self.segwit.values():
|
||||
def write_sign_tx_header(self, w: writers.Writer, has_segwit: bool) -> None:
|
||||
self.write_tx_header(w, self.tx, has_segwit)
|
||||
writers.write_varint(w, self.tx.inputs_count)
|
||||
|
||||
def write_sign_tx_footer(self, w: writers.Writer) -> None:
|
||||
writers.write_uint32(w, self.tx.lock_time)
|
||||
|
||||
def write_tx_header(
|
||||
self, w: writers.Writer, tx: Union[SignTx, TransactionType], has_segwit: bool
|
||||
) -> None:
|
||||
writers.write_uint32(w, tx.version) # nVersion
|
||||
if has_segwit:
|
||||
writers.write_varint(w, 0x00) # segwit witness marker
|
||||
writers.write_varint(w, 0x01) # segwit witness flag
|
||||
writers.write_varint(w, self.tx.inputs_count)
|
||||
|
||||
async def write_prev_tx_footer(
|
||||
self, w: writers.Writer, tx: TransactionType, prev_hash: bytes
|
||||
) -> None:
|
||||
writers.write_uint32(w, tx.lock_time)
|
||||
|
||||
# TX Outputs
|
||||
# ===
|
||||
@ -597,25 +489,7 @@ class Bitcoin:
|
||||
)
|
||||
return scripts.output_script_native_p2wpkh_or_p2wsh(witprog)
|
||||
|
||||
if (
|
||||
not utils.BITCOIN_ONLY
|
||||
and self.coin.cashaddr_prefix is not None
|
||||
and o.address.startswith(self.coin.cashaddr_prefix + ":")
|
||||
):
|
||||
prefix, addr = o.address.split(":")
|
||||
version, data = cashaddr.decode(prefix, addr)
|
||||
if version == cashaddr.ADDRESS_TYPE_P2KH:
|
||||
version = self.coin.address_type
|
||||
elif version == cashaddr.ADDRESS_TYPE_P2SH:
|
||||
version = self.coin.address_type_p2sh
|
||||
else:
|
||||
raise SigningError("Unknown cashaddr address type")
|
||||
raw_address = bytes([version]) + data
|
||||
else:
|
||||
try:
|
||||
raw_address = base58.decode_check(o.address, self.coin.b58_hash)
|
||||
except ValueError:
|
||||
raise SigningError(FailureType.DataError, "Invalid address")
|
||||
raw_address = self.get_raw_address(o)
|
||||
|
||||
if address_type.check(self.coin.address_type, raw_address):
|
||||
# p2pkh
|
||||
@ -631,6 +505,12 @@ class Bitcoin:
|
||||
|
||||
raise SigningError(FailureType.DataError, "Invalid address type")
|
||||
|
||||
def get_raw_address(self, o: TxOutputType) -> bytes:
|
||||
try:
|
||||
return base58.decode_check(o.address, self.coin.b58_hash)
|
||||
except ValueError:
|
||||
raise SigningError(FailureType.DataError, "Invalid address")
|
||||
|
||||
def get_address_for_change(self, o: TxOutputType) -> str:
|
||||
try:
|
||||
input_script_type = helpers.CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES[
|
||||
|
Loading…
Reference in New Issue
Block a user