diff --git a/legacy/firmware/signing.c b/legacy/firmware/signing.c index a2624c229b..dbb05d615e 100644 --- a/legacy/firmware/signing.c +++ b/legacy/firmware/signing.c @@ -194,6 +194,9 @@ static Hasher coinjoin_request_hasher; transaction disables replace-by-fee opt-in. */ #define MAX_BIP125_RBF_SEQUENCE 0xFFFFFFFD +/* supported version of Decred script_version */ +#define DECRED_SCRIPT_VERSION 0 + enum { DECRED_SERIALIZE_FULL = 0, DECRED_SERIALIZE_NO_WITNESS = 1, @@ -1925,6 +1928,85 @@ static bool signing_check_prevtx_hash(void) { return true; } +static bool compile_output(TxOutputType *in, TxOutputBinType *out, + bool needs_confirm) { + memzero(out, sizeof(TxOutputBinType)); + out->amount = in->amount; + out->decred_script_version = DECRED_SCRIPT_VERSION; + + if (in->script_type == OutputScriptType_PAYTOOPRETURN) { + // only 0 satoshi allowed for OP_RETURN + if (in->amount != 0 || in->has_address || (in->address_n_count > 0) || + in->has_multisig) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return false; + } + if (needs_confirm) { + if (in->op_return_data.size >= 8 && + memcmp(in->op_return_data.bytes, "omni", 4) == + 0) { // OMNI transaction + layoutConfirmOmni(in->op_return_data.bytes, in->op_return_data.size); + } else { + layoutConfirmOpReturn(in->op_return_data.bytes, + in->op_return_data.size); + } + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, + false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + signing_abort(); + return false; + } + } + op_return_to_script_pubkey( + in->op_return_data.bytes, in->op_return_data.size, + out->script_pubkey.bytes, &out->script_pubkey.size); + return true; + } + + if (in->address_n_count > 0) { + InputScriptType input_script_type = 0; + + if (!change_output_to_input_script_type(in->script_type, + &input_script_type) || + hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, + NULL) == 0 || + hdnode_fill_public_key(&node) != 0 || + !compute_address(coin, input_script_type, &node, in->has_multisig, + &in->multisig, in->address)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return false; + } + } else if (!in->has_address) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return false; + } + + if (!address_to_script_pubkey(coin, in->address, out->script_pubkey.bytes, + &out->script_pubkey.size)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return false; + } + + if (needs_confirm) { + layoutConfirmOutput(coin, amount_unit, in); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + signing_abort(); + return false; + } + } + + return true; +} + static bool is_change_output(const TxInfo *tx_info, const TxOutputType *txoutput) { if (!is_change_output_script_type(txoutput->script_type)) { @@ -1992,21 +2074,12 @@ static bool signing_add_output(TxOutputType *txoutput) { // Skip confirmation of change-outputs and skip output confirmation altogether // in replacement transactions. bool skip_confirm = is_change || is_replacement || (is_coinjoin == sectrue); - int co = compile_output(coin, amount_unit, &root, txoutput, &bin_output, - !skip_confirm); + if (!compile_output(txoutput, &bin_output, !skip_confirm)) { + return false; + } if (!skip_confirm) { report_progress(true); } - if (co < 0) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - signing_abort(); - return false; - } else if (co == 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Failed to compile output")); - signing_abort(); - return false; - } #if !BITCOIN_ONLY if (coin->decred) { if (serialize) { @@ -2144,11 +2217,7 @@ static bool signing_add_orig_input(TxInputType *orig_input) { static bool signing_add_orig_output(TxOutputType *orig_output) { // Compute scriptPubKey. TxOutputBinType orig_bin_output; - if (compile_output(coin, amount_unit, &root, orig_output, &orig_bin_output, - false) <= 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Failed to compile output")); - signing_abort(); + if (!compile_output(orig_output, &orig_bin_output, false)) { return false; } @@ -2854,11 +2923,7 @@ static bool signing_hash_orig_input(TxInputType *orig_input) { } static bool signing_hash_orig_output(TxOutputType *orig_output) { - if (compile_output(coin, amount_unit, &root, orig_output, &bin_output, - false) <= 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Failed to compile output")); - signing_abort(); + if (!compile_output(orig_output, &bin_output, false)) { return false; } @@ -3821,11 +3886,7 @@ void signing_txack(TransactionType *tx) { } progress_step++; - if (compile_output(coin, amount_unit, &root, tx->outputs, &bin_output, - false) <= 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Failed to compile output")); - signing_abort(); + if (!compile_output(tx->outputs, &bin_output, false)) { return; } // check hashOutputs @@ -3971,11 +4032,7 @@ void signing_txack(TransactionType *tx) { } progress_step++; - if (compile_output(coin, amount_unit, &root, tx->outputs, &bin_output, - false) <= 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Failed to compile output")); - signing_abort(); + if (!compile_output(tx->outputs, &bin_output, false)) { return; } resp.has_serialized = true; diff --git a/legacy/firmware/transaction.c b/legacy/firmware/transaction.c index 7f002675e9..fbf045fed0 100644 --- a/legacy/firmware/transaction.c +++ b/legacy/firmware/transaction.c @@ -74,8 +74,6 @@ /* size of a Decred witness (without script): 8 amount, 4 block height, 4 block * index */ #define TXSIZE_DECRED_WITNESS 16 -/* support version of Decred script_version */ -#define DECRED_SCRIPT_VERSION 0 static const uint8_t segwit_header[2] = {0, 1}; @@ -235,8 +233,8 @@ bool compute_address(const CoinInfo *coin, InputScriptType script_type, return 1; } -static int address_to_script_pubkey(const CoinInfo *coin, const char *address, - uint8_t *script_pubkey, pb_size_t *size) { +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); @@ -323,96 +321,16 @@ static int address_to_script_pubkey(const CoinInfo *coin, const char *address, return 0; } -int compile_output(const CoinInfo *coin, AmountUnit amount_unit, - const HDNode *root, TxOutputType *in, TxOutputBinType *out, - bool needs_confirm) { - memzero(out, sizeof(TxOutputBinType)); - out->amount = in->amount; - out->decred_script_version = DECRED_SCRIPT_VERSION; - - if (in->script_type == OutputScriptType_PAYTOOPRETURN) { - // only 0 satoshi allowed for OP_RETURN - if (in->amount != 0 || in->has_address || (in->address_n_count > 0) || - in->has_multisig) { - return 0; // failed to compile output - } - if (needs_confirm) { - if (in->op_return_data.size >= 8 && - memcmp(in->op_return_data.bytes, "omni", 4) == - 0) { // OMNI transaction - layoutConfirmOmni(in->op_return_data.bytes, in->op_return_data.size); - } else { - layoutConfirmOpReturn(in->op_return_data.bytes, - in->op_return_data.size); - } - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, - false)) { - return -1; // user aborted - } - } - uint32_t r = 0; - out->script_pubkey.bytes[0] = 0x6A; - r++; // OP_RETURN - r += op_push(in->op_return_data.size, out->script_pubkey.bytes + r); - memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, - in->op_return_data.size); - r += in->op_return_data.size; - out->script_pubkey.size = r; - return r; - } - - if (in->address_n_count > 0) { - static CONFIDENTIAL HDNode node; - InputScriptType input_script_type = 0; - - switch (in->script_type) { - case OutputScriptType_PAYTOADDRESS: - input_script_type = InputScriptType_SPENDADDRESS; - break; - case OutputScriptType_PAYTOMULTISIG: - input_script_type = InputScriptType_SPENDMULTISIG; - break; - case OutputScriptType_PAYTOWITNESS: - input_script_type = InputScriptType_SPENDWITNESS; - break; - case OutputScriptType_PAYTOP2SHWITNESS: - input_script_type = InputScriptType_SPENDP2SHWITNESS; - break; - case OutputScriptType_PAYTOTAPROOT: - input_script_type = InputScriptType_SPENDTAPROOT; - break; - default: - return 0; // failed to compile output - } - memcpy(&node, root, sizeof(HDNode)); - if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, - NULL) == 0) { - return 0; // failed to compile output - } - if (hdnode_fill_public_key(&node) != 0) { - return 0; // failed to compile output - } - if (!compute_address(coin, input_script_type, &node, in->has_multisig, - &in->multisig, in->address)) { - return 0; // failed to compile output - } - } else if (!in->has_address) { - return 0; // failed to compile output - } - - if (!address_to_script_pubkey(coin, in->address, out->script_pubkey.bytes, - &out->script_pubkey.size)) { - return 0; - } - - if (needs_confirm) { - layoutConfirmOutput(coin, amount_unit, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; // user aborted - } - } - - return out->script_pubkey.size; +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, diff --git a/legacy/firmware/transaction.h b/legacy/firmware/transaction.h index ce02524d6d..85848a6929 100644 --- a/legacy/firmware/transaction.h +++ b/legacy/firmware/transaction.h @@ -75,6 +75,8 @@ bool compute_address(const CoinInfo *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE]); +int address_to_script_pubkey(const CoinInfo *coin, const char *address, + uint8_t *script_pubkey, pb_size_t *size); uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out); uint32_t compile_script_multisig(const CoinInfo *coin, @@ -101,9 +103,9 @@ bool tx_sign_ecdsa(const ecdsa_curve *curve, const uint8_t *private_key, const uint8_t *hash, uint8_t *out, pb_size_t *size); bool tx_sign_bip340(const uint8_t *private_key, const uint8_t *hash, uint8_t *out, pb_size_t *size); -int compile_output(const CoinInfo *coin, AmountUnit amount_unit, - const HDNode *root, TxOutputType *in, TxOutputBinType *out, - bool needs_confirm); +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); bool get_script_pubkey(const CoinInfo *coin, HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, InputScriptType script_type, uint8_t *script_pubkey,