diff --git a/firmware/protob/types.options b/firmware/protob/types.options index 536e91ab48..bd56519202 100644 --- a/firmware/protob/types.options +++ b/firmware/protob/types.options @@ -12,7 +12,7 @@ TxInputType.address_n max_count:8 TxInputType.prev_hash max_size:32 TxInputType.script_sig max_size:1650 -TxOutputType.address max_size:41 +TxOutputType.address max_size:54 TxOutputType.address_n max_count:8 TxOutputType.op_return_data max_size:80 diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h index a7dd51e8a2..39ddfe3d70 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -229,7 +229,7 @@ typedef struct { typedef struct _TxOutputType { bool has_address; - char address[41]; + char address[54]; size_t address_n_count; uint32_t address_n[8]; uint64_t amount; diff --git a/firmware/transaction.c b/firmware/transaction.c index c4667b963b..fd032067a2 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -61,11 +61,68 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + int addr_raw_len; + + if (in->has_address) { // address provided -> regular output + addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE); + if (in->script_type != OutputScriptType_PAYTOADDRESS) { + // allow for p2sh (backward compatibility only) + if (in->script_type != OutputScriptType_PAYTOSCRIPTHASH + || addr_raw_len != 21 + || addr_raw[0] != coin->address_type_p2sh) { + return 0; + } + } + + if (addr_raw_len == 21 && + addr_raw[0] == coin->address_type) { // p2pkh + + out->script_pubkey.bytes[0] = 0x76; // OP_DUP + out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 3, addr_raw + 1, 20); + out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY + out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG + out->script_pubkey.size = 25; + } else if (addr_raw_len == 21 + && addr_raw[0] == coin->address_type_p2sh) { // p2sh + out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + 1, 20); + out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL + out->script_pubkey.size = 23; + } else if (addr_raw_len == 23 + && addr_raw[0] == coin->address_type_p2wpkh + && addr_raw[1] == 0 && addr_raw[2] == 0) { // p2wpkh v0 + out->script_pubkey.bytes[0] = 0x00; // version 0 + out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + 3, 20); + out->script_pubkey.size = 22; + } else if (addr_raw_len == 35 + && addr_raw[0] == coin->address_type_p2wsh + && addr_raw[1] == 0 && addr_raw[2] == 0) { // p2wsh v0 + out->script_pubkey.bytes[0] = 0x00; // version 0 + out->script_pubkey.bytes[1] = 0x20; // pushing 32 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + 3, 32); + out->script_pubkey.size = 34; + } else { + return 0; + } + + if (needs_confirm) { + layoutConfirmOutput(coin, in); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return -1; + } + } + + return out->script_pubkey.size; + } if (in->script_type == OutputScriptType_PAYTOADDRESS) { - // address_n provided-> change address -> calculate from address_n - if (in->address_n_count > 0) { + if (in->script_type == OutputScriptType_PAYTOADDRESS && + in->address_n_count > 0) { HDNode node; memcpy(&node, root, sizeof(HDNode)); if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count) == 0) { @@ -73,18 +130,6 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T } layoutProgressUpdate(true); hdnode_get_address_raw(&node, coin->address_type, addr_raw); - } else - if (in->has_address) { // address provided -> regular output - if (needs_confirm) { - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; - } - } - if (!ecdsa_address_decode(in->address, coin->address_type, addr_raw)) { - return 0; - } - } else { // does not have address_n neither address -> error return 0; } @@ -99,24 +144,6 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T return 25; } - if (in->script_type == OutputScriptType_PAYTOSCRIPTHASH) { - if (!in->has_address || !ecdsa_address_decode(in->address, coin->address_type_p2sh, addr_raw)) { - return 0; - } - if (needs_confirm) { - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; - } - } - out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + address_prefix_bytes_len(coin->address_type_p2sh), 20); - out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL - out->script_pubkey.size = 23; - return 23; - } - if (in->script_type == OutputScriptType_PAYTOMULTISIG) { uint8_t buf[32]; size_t prefix_bytes = address_prefix_bytes_len(coin->address_type_p2sh);