/* * This file is part of the Trezor project, https://trezor.io/ * * Copyright (C) 2014 Pavol Rusnak * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include "transaction.h" #include #include "address.h" #include "base58.h" #include "coins.h" #include "crypto.h" #include "debug.h" #include "ecdsa.h" #include "fsm.h" #include "gettext.h" #include "layout2.h" #include "memzero.h" #include "messages.pb.h" #include "protect.h" #include "ripemd160.h" #include "secp256k1.h" #include "segwit_addr.h" #include "util.h" #include "zkp_bip340.h" #if !BITCOIN_ONLY #include "cash_addr.h" #endif #define SEGWIT_VERSION_0 0 #define SEGWIT_VERSION_1 1 #define CASHADDR_P2KH (0) #define CASHADDR_P2SH (8) #define CASHADDR_160 (0) /* transaction input size (without script): 32 prevhash, 4 idx, 4 sequence */ #define TXSIZE_INPUT 40 /* transaction output size (without script): 8 amount */ #define TXSIZE_OUTPUT 8 /* size of a pubkey */ #define TXSIZE_PUBKEY 33 /* size of a DER signature (3 type bytes, 3 len bytes, 33 R, 32 S, 1 sighash */ #define TXSIZE_DER_SIGNATURE 72 /* size of a Schnorr signature (32 R, 32 S, no sighash) */ #define TXSIZE_SCHNORR_SIGNATURE 64 /* size of a multiscript without pubkey (1 M, 1 N, 1 checksig) */ #define TXSIZE_MULTISIGSCRIPT 3 /* size of a p2wpkh script (1 version, 1 push, 20 hash) */ #define TXSIZE_WITNESSPKHASH 22 /* size of a p2wsh script (1 version, 1 push, 32 hash) */ #define TXSIZE_WITNESSSCRIPT 34 /* size of a p2tr script (1 version, 1 push, 32 hash) */ #define TXSIZE_TAPROOT 34 /* size of a p2pkh script (dup, hash, push, 20 pubkeyhash, equal, checksig) */ #define TXSIZE_P2PKHASH 25 /* size of a p2sh script (hash, push, 20 scripthash, equal) */ #define TXSIZE_P2SCRIPT 23 /* size of a Decred witness (without script): 8 amount, 4 block height, 4 block * index */ #define TXSIZE_DECRED_WITNESS 16 static const uint8_t segwit_header[2] = {0, 1}; static const uint8_t SLIP19_VERSION_MAGIC[] = {0x53, 0x4c, 0x00, 0x19}; static inline uint32_t op_push_size(uint32_t i) { if (i < 0x4C) { return 1; } if (i < 0x100) { return 2; } if (i < 0x10000) { return 3; } return 5; } uint32_t op_push(uint32_t i, uint8_t *out) { if (i < 0x4C) { out[0] = i & 0xFF; return 1; } if (i < 0x100) { out[0] = 0x4C; out[1] = i & 0xFF; return 2; } if (i < 0x10000) { out[0] = 0x4D; out[1] = i & 0xFF; out[2] = (i >> 8) & 0xFF; return 3; } out[0] = 0x4E; out[1] = i & 0xFF; out[2] = (i >> 8) & 0xFF; out[3] = (i >> 16) & 0xFF; out[4] = (i >> 24) & 0xFF; return 5; } bool compute_address(const CoinInfo *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE]) { uint8_t raw[MAX_ADDR_RAW_SIZE] = {0}; uint8_t digest[32] = {0}; size_t prelen = 0; if (has_multisig) { if (cryptoMultisigPubkeyIndex(coin, multisig, node->public_key) < 0) { return 0; } if (compile_script_multisig_hash(coin, multisig, digest) == 0) { return 0; } if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wsh: script hash is single sha256 if (!coin->has_segwit || !coin->bech32_prefix) { return 0; } if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 32)) { return 0; } } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wsh encapsuled in p2sh address if (!coin->has_segwit) { return 0; } raw[0] = 0; // push version raw[1] = 32; // push 32 bytes memcpy(raw + 2, digest, 32); // push hash hasher_Raw(coin->curve->hasher_pubkey, raw, 34, digest); prelen = address_prefix_bytes_len(coin->address_type_p2sh); address_write_prefix_bytes(coin->address_type_p2sh, raw); memcpy(raw + prelen, digest, 32); if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, address, MAX_ADDR_SIZE)) { return 0; } } else if (script_type == InputScriptType_SPENDADDRESS || script_type == InputScriptType_SPENDMULTISIG) { #if !BITCOIN_ONLY if (coin->cashaddr_prefix) { raw[0] = CASHADDR_P2SH | CASHADDR_160; ripemd160(digest, 32, raw + 1); if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { return 0; } } else #endif { // non-segwit p2sh multisig prelen = address_prefix_bytes_len(coin->address_type_p2sh); address_write_prefix_bytes(coin->address_type_p2sh, raw); ripemd160(digest, 32, raw + prelen); if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, address, MAX_ADDR_SIZE)) { return 0; } } } else { // unsupported script type return 0; } } else if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wpkh: pubkey hash is ripemd160 of sha256 if (!coin->has_segwit || !coin->bech32_prefix) { return 0; } ecdsa_get_pubkeyhash(node->public_key, coin->curve->hasher_pubkey, digest); if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 20)) { return 0; } } else if (script_type == InputScriptType_SPENDTAPROOT) { // taproot if (!coin->has_taproot || !coin->has_segwit || !coin->bech32_prefix) { return 0; } uint8_t tweaked_pubkey[32]; zkp_bip340_tweak_public_key(node->public_key + 1, NULL, tweaked_pubkey); if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_1, tweaked_pubkey, 32)) { return 0; } } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wpkh embedded in p2sh if (!coin->has_segwit) { return 0; } ecdsa_get_address_segwit_p2sh( node->public_key, coin->address_type_p2sh, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); } else if (script_type == InputScriptType_SPENDADDRESS) { #if !BITCOIN_ONLY if (coin->cashaddr_prefix) { ecdsa_get_address_raw(node->public_key, CASHADDR_P2KH | CASHADDR_160, coin->curve->hasher_pubkey, raw); if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { return 0; } } else #endif { ecdsa_get_address(node->public_key, coin->address_type, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); } } else { // unsupported script type return 0; } return 1; } int address_to_script_pubkey(const CoinInfo *coin, const char *address, uint8_t *script_pubkey, pb_size_t *size) { uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0}; size_t addr_raw_len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); // P2PKH size_t prefix_len = address_prefix_bytes_len(coin->address_type); if (addr_raw_len == 20 + prefix_len && address_check_prefix(addr_raw, coin->address_type)) { script_pubkey[0] = 0x76; // OP_DUP script_pubkey[1] = 0xA9; // OP_HASH_160 script_pubkey[2] = 0x14; // pushing 20 bytes memcpy(script_pubkey + 3, addr_raw + prefix_len, 20); script_pubkey[23] = 0x88; // OP_EQUALVERIFY script_pubkey[24] = 0xAC; // OP_CHECKSIG *size = 25; return 1; } // P2SH prefix_len = address_prefix_bytes_len(coin->address_type_p2sh); if (addr_raw_len == 20 + prefix_len && address_check_prefix(addr_raw, coin->address_type_p2sh)) { script_pubkey[0] = 0xA9; // OP_HASH_160 script_pubkey[1] = 0x14; // pushing 20 bytes memcpy(script_pubkey + 2, addr_raw + prefix_len, 20); script_pubkey[22] = 0x87; // OP_EQUAL *size = 23; return 1; } #if !BITCOIN_ONLY if (coin->cashaddr_prefix && cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, address)) { if (addr_raw_len == 21 && addr_raw[0] == (CASHADDR_P2KH | CASHADDR_160)) { script_pubkey[0] = 0x76; // OP_DUP script_pubkey[1] = 0xA9; // OP_HASH_160 script_pubkey[2] = 0x14; // pushing 20 bytes memcpy(script_pubkey + 3, addr_raw + 1, 20); script_pubkey[23] = 0x88; // OP_EQUALVERIFY script_pubkey[24] = 0xAC; // OP_CHECKSIG *size = 25; return 1; } else if (addr_raw_len == 21 && addr_raw[0] == (CASHADDR_P2SH | CASHADDR_160)) { script_pubkey[0] = 0xA9; // OP_HASH_160 script_pubkey[1] = 0x14; // pushing 20 bytes memcpy(script_pubkey + 2, addr_raw + 1, 20); script_pubkey[22] = 0x87; // OP_EQUAL *size = 23; return 1; } else { return 0; } } #endif // SegWit if (coin->bech32_prefix) { int witver = 0; if (!segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, address)) { return 0; } // check that the witness version is recognized if (witver != 0 && witver != 1) { return 0; } // check that P2TR address encodes a valid BIP340 public key if (witver == 1) { if (addr_raw_len != 32 || zkp_bip340_verify_publickey(addr_raw) != 0) { return 0; } } // push 1 byte version id (opcode OP_0 = 0, OP_i = 80+i) // push addr_raw (segwit_addr_decode makes sure addr_raw_len is at most 40) script_pubkey[0] = witver == 0 ? 0 : 80 + witver; script_pubkey[1] = addr_raw_len; memcpy(script_pubkey + 2, addr_raw, addr_raw_len); *size = addr_raw_len + 2; return 1; } return 0; } void op_return_to_script_pubkey(const uint8_t *op_return_data, size_t op_return_size, uint8_t *script_pubkey, pb_size_t *script_pubkey_size) { uint32_t r = 0; script_pubkey[0] = 0x6A; r++; // OP_RETURN r += op_push(op_return_size, script_pubkey + r); memcpy(script_pubkey + r, op_return_data, op_return_size); r += op_return_size; *script_pubkey_size = r; } bool get_script_pubkey(const CoinInfo *coin, HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, InputScriptType script_type, uint8_t *script_pubkey, pb_size_t *script_pubkey_size) { char address[MAX_ADDR_SIZE] = {0}; bool res = true; res = res && (hdnode_fill_public_key(node) == 0); res = res && compute_address(coin, script_type, node, has_multisig, multisig, address); res = res && address_to_script_pubkey(coin, address, script_pubkey, script_pubkey_size); return res; } uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out) { if (coinByAddressType(address_type)) { // valid coin type out[0] = 0x76; // OP_DUP out[1] = 0xA9; // OP_HASH_160 out[2] = 0x14; // pushing 20 bytes memcpy(out + 3, pubkeyhash, 20); out[23] = 0x88; // OP_EQUALVERIFY out[24] = 0xAC; // OP_CHECKSIG return 25; } else { return 0; // unsupported } } // if out == NULL just compute the length uint32_t compile_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *out) { if (multisig->pubkeys_order != MultisigPubkeysOrder_PRESERVED) { fsm_sendFailure(FailureType_Failure_DataError, _("Sortedmulti is not supported")); return 0; } const uint32_t m = multisig->m; const uint32_t n = cryptoMultisigPubkeyCount(multisig); if (m < 1 || m > 15) return 0; if (n < 1 || n > 15) return 0; uint32_t r = 0; // TODO if (out) { out[r] = 0x50 + m; r++; for (uint32_t i = 0; i < n; i++) { out[r] = 33; r++; // OP_PUSH 33 const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); if (!pubnode) return 0; memcpy(out + r, pubnode->public_key, 33); r += 33; } out[r] = 0x50 + n; r++; out[r] = 0xAE; r++; // OP_CHECKMULTISIG } else { r = 1 + 34 * n + 2; } return r; } uint32_t compile_script_multisig_hash(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *hash) { if (multisig->pubkeys_order != MultisigPubkeysOrder_PRESERVED) { fsm_sendFailure(FailureType_Failure_DataError, _("Sortedmulti is not supported")); return 0; } const uint32_t m = multisig->m; const uint32_t n = cryptoMultisigPubkeyCount(multisig); if (m < 1 || m > 15) return 0; if (n < 1 || n > 15) return 0; Hasher hasher = {0}; hasher_Init(&hasher, coin->curve->hasher_script); uint8_t d[2] = {0}; d[0] = 0x50 + m; hasher_Update(&hasher, d, 1); for (uint32_t i = 0; i < n; i++) { d[0] = 33; hasher_Update(&hasher, d, 1); // OP_PUSH 33 const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); if (!pubnode) return 0; hasher_Update(&hasher, pubnode->public_key, 33); } d[0] = 0x50 + n; d[1] = 0xAE; hasher_Update(&hasher, d, 2); hasher_Final(&hasher, hash); return 1; } uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t sighash, uint8_t *out) { uint32_t r = 0; r += op_push(signature_len + 1, out + r); memcpy(out + r, signature, signature_len); r += signature_len; out[r] = sighash; r++; r += op_push(pubkey_len, out + r); memcpy(out + r, pubkey, pubkey_len); r += pubkey_len; return r; } uint32_t serialize_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out) { if (multisig->pubkeys_order != MultisigPubkeysOrder_PRESERVED) { fsm_sendFailure(FailureType_Failure_DataError, _("Sortedmulti is not supported")); return 0; } uint32_t r = 0; #if !BITCOIN_ONLY if (!coin->decred) { // Decred fixed the off-by-one bug #endif out[r] = 0x00; r++; #if !BITCOIN_ONLY } #endif for (uint32_t i = 0; i < multisig->signatures_count; i++) { if (multisig->signatures[i].size == 0) { continue; } r += op_push(multisig->signatures[i].size + 1, out + r); memcpy(out + r, multisig->signatures[i].bytes, multisig->signatures[i].size); r += multisig->signatures[i].size; out[r] = sighash; r++; } uint32_t script_len = compile_script_multisig(coin, multisig, 0); if (script_len == 0) { return 0; } r += op_push(script_len, out + r); r += compile_script_multisig(coin, multisig, out + r); return r; } uint32_t serialize_p2wpkh_witness(const uint8_t *signature, uint32_t signature_len, const uint8_t *public_key, uint32_t public_key_len, uint8_t sighash, uint8_t *out) { uint32_t r = 0; // 2 stack items r += ser_length(2, out + r); // length-prefixed signature with sighash type r += ser_length(signature_len + 1, out + r); memcpy(out + r, signature, signature_len); r += signature_len; out[r] = sighash; r += 1; // length-prefixed public key r += tx_serialize_script(public_key_len, public_key, out + r); return r; } uint32_t serialize_p2tr_witness(const uint8_t *signature, uint32_t signature_len, uint8_t sighash, uint8_t *out) { uint32_t r = 0; // 1 stack item r += ser_length(1, out + r); // length-prefixed signature with optional sighash type uint32_t sighash_len = sighash ? 1 : 0; r += ser_length(signature_len + sighash_len, out + r); memcpy(out + r, signature, signature_len); r += signature_len; if (sighash) { out[r] = sighash; r += 1; } return r; } bool tx_sign_ecdsa(const ecdsa_curve *curve, const uint8_t *private_key, const uint8_t *hash, uint8_t *out, pb_size_t *size) { uint8_t signature[64] = {0}; if (ecdsa_sign_digest(curve, private_key, hash, signature, NULL, NULL) != 0) { return false; } *size = ecdsa_sig_to_der(signature, out); return true; } bool tx_sign_bip340(const uint8_t *private_key, const uint8_t *hash, uint8_t *out, pb_size_t *size) { static CONFIDENTIAL uint8_t output_private_key[32] = {0}; bool ret = (zkp_bip340_tweak_private_key(private_key, NULL, output_private_key) == 0); ret = ret && (zkp_bip340_sign_digest(output_private_key, hash, out, NULL) == 0); *size = ret ? 64 : 0; memzero(output_private_key, sizeof(output_private_key)); return ret; } // tx methods bool tx_input_check_hash(Hasher *hasher, const TxInputType *input) { hasher_Update(hasher, (const uint8_t *)&input->address_n_count, sizeof(input->address_n_count)); for (int i = 0; i < input->address_n_count; ++i) hasher_Update(hasher, (const uint8_t *)&input->address_n[i], sizeof(input->address_n[0])); hasher_Update(hasher, input->prev_hash.bytes, sizeof(input->prev_hash.bytes)); hasher_Update(hasher, (const uint8_t *)&input->prev_index, sizeof(input->prev_index)); tx_script_hash(hasher, input->script_sig.size, input->script_sig.bytes); hasher_Update(hasher, (const uint8_t *)&input->sequence, sizeof(input->sequence)); hasher_Update(hasher, (const uint8_t *)&input->script_type, sizeof(input->script_type)); uint8_t multisig_fp[32] = {0}; if (input->has_multisig) { if (cryptoMultisigFingerprint(&input->multisig, multisig_fp) == 0) { // Invalid multisig parameters. return false; } } hasher_Update(hasher, multisig_fp, sizeof(multisig_fp)); hasher_Update(hasher, (const uint8_t *)&input->amount, sizeof(input->amount)); tx_script_hash(hasher, input->witness.size, input->witness.bytes); hasher_Update(hasher, (const uint8_t *)&input->has_orig_hash, sizeof(input->has_orig_hash)); hasher_Update(hasher, input->orig_hash.bytes, sizeof(input->orig_hash.bytes)); hasher_Update(hasher, (const uint8_t *)&input->orig_index, sizeof(input->orig_index)); tx_script_hash(hasher, input->script_pubkey.size, input->script_pubkey.bytes); return true; } uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input) { for (int i = 0; i < 32; i++) { hasher_Update(hasher, &(input->prev_hash.bytes[31 - i]), 1); } hasher_Update(hasher, (const uint8_t *)&input->prev_index, 4); return 36; } uint32_t tx_amount_hash(Hasher *hasher, const TxInputType *input) { hasher_Update(hasher, (const uint8_t *)&input->amount, 8); return 8; } uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data) { int r = ser_length_hash(hasher, size); hasher_Update(hasher, data, size); return r + size; } uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input) { hasher_Update(hasher, (const uint8_t *)&input->sequence, 4); return 4; } uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, bool decred) { uint32_t r = 0; hasher_Update(hasher, (const uint8_t *)&output->amount, 8); r += 8; if (decred) { uint16_t script_version = output->decred_script_version & 0xFFFF; hasher_Update(hasher, (const uint8_t *)&script_version, 2); r += 2; } r += tx_script_hash(hasher, output->script_pubkey.size, output->script_pubkey.bytes); return r; } uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) { int r = ser_length(size, out); memcpy(out + r, data, size); return r + size; } uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { int r = 0; #if !BITCOIN_ONLY if (tx->is_zcashlike && tx->version >= 3) { uint32_t ver = tx->version | TX_OVERWINTERED; memcpy(out + r, &ver, 4); r += 4; memcpy(out + r, &(tx->version_group_id), 4); r += 4; if (tx->version == 5) { memcpy(out + r, &(tx->branch_id), 4); r += 4; memcpy(out + r, &(tx->lock_time), 4); r += 4; memcpy(out + r, &(tx->expiry), 4); r += 4; } } else #endif { memcpy(out + r, &(tx->version), 4); r += 4; #if !BITCOIN_ONLY if (tx->timestamp) { memcpy(out + r, &(tx->timestamp), 4); r += 4; } #endif if (tx->is_segwit) { memcpy(out + r, segwit_header, 2); r += 2; } } return r + ser_length(tx->inputs_len, out + r); } uint32_t tx_serialize_header_hash(TxStruct *tx) { int r = 4; #if !BITCOIN_ONLY if (tx->is_zcashlike && tx->version >= 3) { uint32_t ver = tx->version | TX_OVERWINTERED; hasher_Update(&(tx->hasher), (const uint8_t *)&ver, 4); hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version_group_id), 4); r += 4; } else #endif { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version), 4); #if !BITCOIN_ONLY if (tx->timestamp) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->timestamp), 4); } #endif if (tx->is_segwit) { hasher_Update(&(tx->hasher), segwit_header, 2); r += 2; } } return r + ser_length_hash(&(tx->hasher), tx->inputs_len); } uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out) { if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; } uint32_t r = 0; if (tx->have_inputs == 0) { r += tx_serialize_header(tx, out + r); } for (int i = 0; i < 32; i++) { *(out + r + i) = input->prev_hash.bytes[31 - i]; } r += 32; memcpy(out + r, &input->prev_index, 4); r += 4; #if !BITCOIN_ONLY if (tx->is_decred) { uint8_t tree = input->decred_tree & 0xFF; out[r++] = tree; } else #endif { r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); } memcpy(out + r, &input->sequence, 4); r += 4; tx->have_inputs++; tx->size += r; return r; } uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) { if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; } uint32_t r = 0; if (tx->have_inputs == 0) { r += tx_serialize_header_hash(tx); } r += tx_prevout_hash(&(tx->hasher), input); #if !BITCOIN_ONLY if (tx->is_decred) { uint8_t tree = input->decred_tree & 0xFF; hasher_Update(&(tx->hasher), (const uint8_t *)&(tree), 1); r++; } else #endif { r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); } r += tx_sequence_hash(&(tx->hasher), input); tx->have_inputs++; tx->size += r; return r; } #if !BITCOIN_ONLY uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, uint8_t *out) { static const uint64_t amount = 0; static const uint32_t block_height = 0x00000000; static const uint32_t block_index = 0xFFFFFFFF; if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; } uint32_t r = 0; if (tx->have_inputs == 0) { r += ser_length(tx->inputs_len, out + r); } if (input->has_amount) { memcpy(out + r, &input->amount, 8); } else { memcpy(out + r, &amount, 8); } r += 8; memcpy(out + r, &block_height, 4); r += 4; memcpy(out + r, &block_index, 4); r += 4; r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); tx->have_inputs++; tx->size += r; return r; } uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, const TxInputType *input) { if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; } uint32_t r = 0; if (tx->have_inputs == 0) { r += tx_serialize_header_hash(tx); } if (input == NULL) { r += ser_length_hash(&(tx->hasher), 0); } else { r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); } tx->have_inputs++; tx->size += r; return r; } #endif uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out) { return ser_length(tx->outputs_len, out); } uint32_t tx_serialize_middle_hash(TxStruct *tx) { return ser_length_hash(&(tx->hasher), tx->outputs_len); } uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { uint32_t r = 0; #if !BITCOIN_ONLY if (tx->is_zcashlike) { if (tx->version == 4) { memcpy(out, &(tx->lock_time), 4); r += 4; memcpy(out + r, &(tx->expiry), 4); r += 4; memzero(out + r, 8); // valueBalance r += 8; out[r] = 0x00; // nShieldedSpend r += 1; out[r] = 0x00; // nShieldedOutput r += 1; out[r] = 0x00; // nJoinSplit r += 1; } else if (tx->version == 5) { out[r] = 0x00; // nSpendsSapling r += 1; out[r] = 0x00; // nOutputsSapling r += 1; out[r] = 0x00; // nActionsOrchard r += 1; } } else if (tx->is_decred) { memcpy(out, &(tx->lock_time), 4); r += 4; memcpy(out + r, &(tx->expiry), 4); r += 4; } else #endif { memcpy(out, &(tx->lock_time), 4); r += 4; } return r; } uint32_t tx_serialize_footer_hash(TxStruct *tx) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->lock_time), 4); #if !BITCOIN_ONLY if (tx->is_zcashlike && tx->version >= 3) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); return 8; } if (tx->is_decred) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); return 8; } #endif return 4; } uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_t *out) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided return 0; } if (tx->have_outputs >= tx->outputs_len) { // already got all outputs return 0; } uint32_t r = 0; if (tx->have_outputs == 0) { r += tx_serialize_middle(tx, out + r); } memcpy(out + r, &output->amount, 8); r += 8; #if !BITCOIN_ONLY if (tx->is_decred) { uint16_t script_version = output->decred_script_version & 0xFFFF; memcpy(out + r, &script_version, 2); r += 2; } #endif r += tx_serialize_script(output->script_pubkey.size, output->script_pubkey.bytes, out + r); tx->have_outputs++; if (tx->have_outputs == tx->outputs_len && !tx->is_segwit) { r += tx_serialize_footer(tx, out + r); } tx->size += r; return r; } uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided return 0; } if (tx->have_outputs >= tx->outputs_len) { // already got all outputs return 0; } uint32_t r = 0; if (tx->have_outputs == 0) { r += tx_serialize_middle_hash(tx); } r += tx_output_hash(&(tx->hasher), output, tx->is_decred); tx->have_outputs++; if (tx->have_outputs == tx->outputs_len && !tx->is_segwit) { r += tx_serialize_footer_hash(tx); } tx->size += r; return r; } #if !BITCOIN_ONLY uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_t datalen) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided return 0; } if (tx->have_outputs < tx->outputs_len) { // not all inputs provided return 0; } if (tx->extra_data_received + datalen > tx->extra_data_len) { // we are receiving too much data return 0; } hasher_Update(&(tx->hasher), data, datalen); tx->extra_data_received += datalen; tx->size += datalen; return datalen; } #endif void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t branch_id, uint32_t extra_data_len, HasherType hasher_sign, bool is_zcashlike, uint32_t version_group_id, uint32_t timestamp) { tx->inputs_len = inputs_len; tx->outputs_len = outputs_len; tx->version = version; tx->lock_time = lock_time; tx->expiry = expiry; tx->branch_id = branch_id; tx->have_inputs = 0; tx->have_outputs = 0; tx->extra_data_len = extra_data_len; tx->extra_data_received = 0; tx->size = 0; tx->is_segwit = false; tx->is_decred = false; tx->is_zcashlike = is_zcashlike; tx->version_group_id = version_group_id; tx->timestamp = timestamp; hasher_Init(&(tx->hasher), hasher_sign); } void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { hasher_Final(&(t->hasher), hash); if (!reverse) return; for (uint8_t i = 0; i < 16; i++) { uint8_t k = hash[31 - i]; hash[31 - i] = hash[i]; hash[i] = k; } } static uint32_t tx_input_script_size(const TxInputType *txinput, InputScriptType script_type) { uint32_t input_script_size = 0; if (txinput->has_multisig) { uint32_t multisig_script_size = TXSIZE_MULTISIGSCRIPT + cryptoMultisigPubkeyCount(&(txinput->multisig)) * (1 + TXSIZE_PUBKEY); if (script_type == InputScriptType_SPENDWITNESS || script_type == InputScriptType_SPENDP2SHWITNESS) { multisig_script_size += ser_length_size(multisig_script_size); } else { multisig_script_size += op_push_size(multisig_script_size); } input_script_size = 1 // the OP_FALSE bug in multisig + txinput->multisig.m * (1 + TXSIZE_DER_SIGNATURE) + multisig_script_size; } else if (script_type == InputScriptType_SPENDTAPROOT) { input_script_size = 1 + TXSIZE_SCHNORR_SIGNATURE; } else { input_script_size = (1 + TXSIZE_DER_SIGNATURE + 1 + TXSIZE_PUBKEY); } return input_script_size; } uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput) { #if !BITCOIN_ONLY if (coin->decred) { return 4 * (TXSIZE_INPUT + 1); // Decred tree } #else (void)coin; #endif InputScriptType script_type = txinput->script_type; if (script_type == InputScriptType_EXTERNAL) { // Guess the script type from the scriptPubKey. switch (txinput->script_pubkey.bytes[0]) { case 0x76: // OP_DUP (P2PKH) script_type = InputScriptType_SPENDADDRESS; break; case 0xA9: // OP_HASH_160 (P2SH, probably nested P2WPKH) script_type = InputScriptType_SPENDP2SHWITNESS; break; case 0x00: // SegWit v0 (probably P2WPKH) script_type = InputScriptType_SPENDWITNESS; break; case 0x51: // SegWit v1 (P2TR) script_type = InputScriptType_SPENDTAPROOT; break; default: // Unknown script type. break; } } uint32_t input_script_size = tx_input_script_size(txinput, script_type); uint32_t weight = 4 * TXSIZE_INPUT; if (script_type == InputScriptType_SPENDADDRESS || script_type == InputScriptType_SPENDMULTISIG) { input_script_size += ser_length_size(input_script_size); weight += 4 * input_script_size; } else if (script_type == InputScriptType_SPENDWITNESS || script_type == InputScriptType_SPENDTAPROOT || script_type == InputScriptType_SPENDP2SHWITNESS) { if (script_type == InputScriptType_SPENDP2SHWITNESS) { weight += 4 * (2 + (txinput->has_multisig ? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH)); } else { weight += 4; // empty input script } weight += input_script_size; // discounted witness } return weight; } uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput) { uint32_t output_script_size = 0; if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) { output_script_size = 1 + op_push_size(txoutput->op_return_data.size) + txoutput->op_return_data.size; } else if (txoutput->address_n_count > 0) { if (txoutput->script_type == OutputScriptType_PAYTOWITNESS) { output_script_size = txoutput->has_multisig ? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH; } else if (txoutput->script_type == OutputScriptType_PAYTOTAPROOT) { output_script_size = TXSIZE_TAPROOT; } else if (txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) { output_script_size = TXSIZE_P2SCRIPT; } else { output_script_size = txoutput->has_multisig ? TXSIZE_P2SCRIPT : TXSIZE_P2PKHASH; } } else { uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0}; int witver = 0; size_t addr_raw_len = 0; #if !BITCOIN_ONLY if (coin->cashaddr_prefix && cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, txoutput->address)) { if (addr_raw_len == 21 && addr_raw[0] == (CASHADDR_P2KH | CASHADDR_160)) { output_script_size = TXSIZE_P2PKHASH; } else if (addr_raw_len == 21 && addr_raw[0] == (CASHADDR_P2SH | CASHADDR_160)) { output_script_size = TXSIZE_P2SCRIPT; } } else #endif { if (coin->bech32_prefix && segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, txoutput->address)) { output_script_size = 2 + addr_raw_len; } else { addr_raw_len = base58_decode_check(txoutput->address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); if (address_check_prefix(addr_raw, coin->address_type)) { output_script_size = TXSIZE_P2PKHASH; } else if (address_check_prefix(addr_raw, coin->address_type_p2sh)) { output_script_size = TXSIZE_P2SCRIPT; } } } } output_script_size += ser_length_size(output_script_size); uint32_t size = TXSIZE_OUTPUT; #if !BITCOIN_ONLY if (coin->decred) { size += 2; // Decred script version } #endif return 4 * (size + output_script_size); } #if !BITCOIN_ONLY uint32_t tx_decred_witness_weight(const TxInputType *txinput) { uint32_t input_script_size = tx_input_script_size(txinput, txinput->script_type); if (txinput->script_type == InputScriptType_SPENDMULTISIG) { // Decred fixed the the OP_FALSE bug in multisig. input_script_size -= 1; // Subtract one OP_FALSE byte. } uint32_t size = TXSIZE_DECRED_WITNESS + ser_length_size(input_script_size) + input_script_size; return 4 * size; } #endif bool get_ownership_proof(const CoinInfo *coin, InputScriptType script_type, const HDNode *node, uint8_t flags, const uint8_t ownership_id[OWNERSHIP_ID_SIZE], const uint8_t *script_pubkey, size_t script_pubkey_size, const uint8_t *commitment_data, size_t commitment_data_size, OwnershipProof *out) { size_t r = 0; // Write versionMagic (4 bytes). memcpy(out->ownership_proof.bytes + r, SLIP19_VERSION_MAGIC, sizeof(SLIP19_VERSION_MAGIC)); r += sizeof(SLIP19_VERSION_MAGIC); // Write flags (1 byte). out->ownership_proof.bytes[r] = flags; r += 1; // Write number of ownership IDs (1 byte). r += ser_length(1, out->ownership_proof.bytes + r); // Write ownership ID (32 bytes). memcpy(out->ownership_proof.bytes + r, ownership_id, OWNERSHIP_ID_SIZE); r += OWNERSHIP_ID_SIZE; // Compute sighash = SHA-256(proofBody || proofFooter). Hasher hasher = {0}; uint8_t sighash[SHA256_DIGEST_LENGTH] = {0}; hasher_InitParam(&hasher, HASHER_SHA2, NULL, 0); hasher_Update(&hasher, out->ownership_proof.bytes, r); tx_script_hash(&hasher, script_pubkey_size, script_pubkey); tx_script_hash(&hasher, commitment_data_size, commitment_data); hasher_Final(&hasher, sighash); // Write proofSignature. if (script_type == InputScriptType_SPENDWITNESS) { if (!tx_sign_ecdsa(coin->curve->params, node->private_key, sighash, out->signature.bytes, &out->signature.size)) { return false; } // Write length-prefixed empty scriptSig (1 byte). r += ser_length(0, out->ownership_proof.bytes + r); // Write // 1. number of stack items (1 byte) // 2. signature + sighash type length (1 byte) // 3. DER-encoded signature (max. 71 bytes) // 4. sighash type (1 byte) // 5. public key length (1 byte) // 6. public key (33 bytes) r += serialize_p2wpkh_witness(out->signature.bytes, out->signature.size, node->public_key, 33, SIGHASH_ALL, out->ownership_proof.bytes + r); } else if (script_type == InputScriptType_SPENDTAPROOT) { if (!tx_sign_bip340(node->private_key, sighash, out->signature.bytes, &out->signature.size)) { return false; } // Write length-prefixed empty scriptSig (1 byte). r += ser_length(0, out->ownership_proof.bytes + r); // Write // 1. number of stack items (1 byte) // 2. signature length (1 byte) // 3. signature (64 bytes) r += serialize_p2tr_witness(out->signature.bytes, out->signature.size, 0, out->ownership_proof.bytes + r); } else { return false; } out->ownership_proof.size = r; return true; } bool tx_input_verify_nonownership( const CoinInfo *coin, const TxInputType *txinput, const uint8_t ownership_id[OWNERSHIP_ID_SIZE]) { size_t r = 0; // Check versionMagic. if (txinput->ownership_proof.size < r + sizeof(SLIP19_VERSION_MAGIC) || memcmp(txinput->ownership_proof.bytes + r, SLIP19_VERSION_MAGIC, sizeof(SLIP19_VERSION_MAGIC)) != 0) { return false; } r += sizeof(SLIP19_VERSION_MAGIC); // Skip flags. r += 1; // Ensure that there is only one ownership ID. if (txinput->ownership_proof.size < r + 1 || txinput->ownership_proof.bytes[r] != 1) { return false; } r += 1; // Ensure that the ownership ID is not ours. if (txinput->ownership_proof.size < r + OWNERSHIP_ID_SIZE || memcmp(txinput->ownership_proof.bytes + r, ownership_id, OWNERSHIP_ID_SIZE) == 0) { return false; } r += OWNERSHIP_ID_SIZE; // Compute the ownership proof digest. Hasher hasher = {0}; hasher_InitParam(&hasher, HASHER_SHA2, NULL, 0); hasher_Update(&hasher, txinput->ownership_proof.bytes, r); tx_script_hash(&hasher, txinput->script_pubkey.size, txinput->script_pubkey.bytes); tx_script_hash(&hasher, txinput->commitment_data.size, txinput->commitment_data.bytes); uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; hasher_Final(&hasher, digest); // Ensure that there is no scriptSig, since we only support native SegWit // ownership proofs. if (txinput->ownership_proof.size < r + 1 || txinput->ownership_proof.bytes[r] != 0) { return false; } r += 1; if (txinput->script_pubkey.size == 22 && memcmp(txinput->script_pubkey.bytes, "\x00\x14", 2) == 0) { // SegWit v0 (probably P2WPKH) const uint8_t *pubkey_hash = txinput->script_pubkey.bytes + 2; // Ensure that there are two stack items. if (txinput->ownership_proof.size < r + 1 || txinput->ownership_proof.bytes[r] != 2) { return false; } r += 1; // Read the signature. if (txinput->ownership_proof.size < r + 1) { return false; } size_t signature_size = txinput->ownership_proof.bytes[r]; r += 1; uint8_t signature[64] = {0}; if (txinput->ownership_proof.size < r + signature_size || ecdsa_sig_from_der(txinput->ownership_proof.bytes + r, signature_size - 1, signature) != 0) { return false; } r += signature_size; // Read the public key. if (txinput->ownership_proof.size < r + 34 || txinput->ownership_proof.bytes[r] != 33) { return false; } const uint8_t *public_key = txinput->ownership_proof.bytes + r + 1; r += 34; // Check the public key matches the scriptPubKey. uint8_t expected_pubkey_hash[20] = {0}; ecdsa_get_pubkeyhash(public_key, coin->curve->hasher_pubkey, expected_pubkey_hash); if (memcmp(pubkey_hash, expected_pubkey_hash, sizeof(expected_pubkey_hash)) != 0) { return false; } // Ensure that we have read the entire ownership proof. if (r != txinput->ownership_proof.size) { return false; } if (ecdsa_verify_digest(coin->curve->params, public_key, signature, digest) != 0) { return false; } } else if (txinput->script_pubkey.size == 34 && memcmp(txinput->script_pubkey.bytes, "\x51\x20", 2) == 0) { // SegWit v1 (P2TR) const uint8_t *output_public_key = txinput->script_pubkey.bytes + 2; // Ensure that there is one stack item consisting of 64 bytes. if (txinput->ownership_proof.size < r + 2 || memcmp(txinput->ownership_proof.bytes + r, "\x01\x40", 2) != 0) { return false; } r += 2; // Read the signature. const uint8_t *signature = txinput->ownership_proof.bytes + r; r += 64; // Ensure that we have read the entire ownership proof. if (r != txinput->ownership_proof.size) { return false; } if (zkp_bip340_verify_digest(output_public_key, signature, digest) != 0) { return false; } } else { // Unsupported script type. return false; } return true; }