diff --git a/CMakeLists.txt b/CMakeLists.txt index 99c31e9b..9123b96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.7.2) project( KeepKeyFirmware - VERSION 7.8.0 + VERSION 7.9.1 LANGUAGES C CXX ASM) set(BOOTLOADER_MAJOR_VERSION 2) diff --git a/deps/device-protocol b/deps/device-protocol index 22ca1d98..f534ff20 160000 --- a/deps/device-protocol +++ b/deps/device-protocol @@ -1 +1 @@ -Subproject commit 22ca1d983c3f513f338e2a0ba997427659646523 +Subproject commit f534ff20fddb63d274eee866acdadad65ca35cf6 diff --git a/include/keepkey/firmware/coins.def b/include/keepkey/firmware/coins.def index 1817b6bd..7b29f12b 100644 --- a/include/keepkey/firmware/coins.def +++ b/include/keepkey/firmware/coins.def @@ -45,6 +45,7 @@ X(true, "THORChain", true, "RUNE", false, NA, false, NA, false, N X(true, "Terra", true, "LUNA", false, NA, false, NA, false, NA, false, {0}, true, 0x8000014a, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "terra", false, false, false, 0, false, 0, false, "" ) X(true, "Kava", true, "KAVA", false, NA, false, NA, false, NA, false, {0}, true, 0x800001cb, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "kava", false, false, false, 0, false, 0, false, "" ) X(true, "Secret", true, "SCRT", false, NA, false, NA, false, NA, false, {0}, true, 0x80000211, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "secret", false, false, false, 0, false, 0, false, "" ) +X(true, "MAYAChain", true, "CACAO", false, NA, false, NA, false, NA, false, {0}, true, 0x800003a3, false, 0, true, 10, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "maya", false, false, false, 0, false, 0, false, "" ) #undef X #undef NO_CONTRACT diff --git a/include/keepkey/firmware/fsm.h b/include/keepkey/firmware/fsm.h index 0aaafcc4..19e91110 100644 --- a/include/keepkey/firmware/fsm.h +++ b/include/keepkey/firmware/fsm.h @@ -114,6 +114,10 @@ void fsm_msgThorchainGetAddress(const ThorchainGetAddress *msg); void fsm_msgThorchainSignTx(const ThorchainSignTx *msg); void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg); +void fsm_msgMayachainGetAddress(const MayachainGetAddress *msg); +void fsm_msgMayachainSignTx(const MayachainSignTx *msg); +void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg); + #if DEBUG_LINK // void fsm_msgDebugLinkDecision(DebugLinkDecision *msg); void fsm_msgDebugLinkGetState(DebugLinkGetState *msg); diff --git a/include/keepkey/firmware/mayachain.h b/include/keepkey/firmware/mayachain.h new file mode 100644 index 00000000..f2942f21 --- /dev/null +++ b/include/keepkey/firmware/mayachain.h @@ -0,0 +1,30 @@ +#ifndef KEEPKEY_FIRMWARE_MAYACHAIN_H +#define KEEPKEY_FIRMWARE_MAYACHAIN_H + +#include "messages.pb.h" +#include "trezor/crypto/bip32.h" + +#include +#include + +typedef struct _MayachainSignTx MayachainSignTx; +typedef struct _MayachainMsgDeposit MayachainMsgDeposit; + +bool mayachain_signTxInit(const HDNode *_node, const MayachainSignTx *_msg); +bool mayachain_signTxUpdateMsgSend(const uint64_t amount, const char *to_address); +bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit *depmsg); +bool mayachain_signTxFinalize(uint8_t *public_key, uint8_t *signature); +bool mayachain_signingIsInited(void); +bool mayachain_signingIsFinished(void); +void mayachain_signAbort(void); +const MayachainSignTx *mayachain_getMayachainSignTx(void); + +// Mayachain swap data parse and confirm +// input: +// swapStr - string in mayachain swap format +// size - size of input string (must be <= 256) +// output: +// true if mayachain data parsed and confirmed by user, false otherwise +bool mayachain_parseConfirmMemo(const char *swapStr, size_t size); + +#endif diff --git a/include/keepkey/transport/interface.h b/include/keepkey/transport/interface.h index 1a3b7e34..ffebf205 100644 --- a/include/keepkey/transport/interface.h +++ b/include/keepkey/transport/interface.h @@ -34,6 +34,7 @@ #include "messages-ripple.pb.h" #include "messages-tendermint.pb.h" #include "messages-thorchain.pb.h" +#include "messages-mayachain.pb.h" #include "types.pb.h" #include "trezor_transport.h" diff --git a/include/keepkey/transport/messages-mayachain.options b/include/keepkey/transport/messages-mayachain.options new file mode 100644 index 00000000..eff57e25 --- /dev/null +++ b/include/keepkey/transport/messages-mayachain.options @@ -0,0 +1,17 @@ +MayachainGetAddress.address_n max_count:10 + +MayachainAddress.address max_size:46 + +MayachainSignTx.address_n max_count:10 +MayachainSignTx.chain_id max_size:32 +MayachainSignTx.memo max_size:256 + +MayachainMsgSend.from_address max_size:46 +MayachainMsgSend.to_address max_size:46 + +MayachainMsgDeposit.asset max_size:20 +MayachainMsgDeposit.memo max_size:256 +MayachainMsgDeposit.signer max_size:46 + +MayachainSignedTx.public_key max_size:33 +MayachainSignedTx.signature max_size:64 diff --git a/lib/firmware/CMakeLists.txt b/lib/firmware/CMakeLists.txt index ee916b72..e08ccfc6 100644 --- a/lib/firmware/CMakeLists.txt +++ b/lib/firmware/CMakeLists.txt @@ -21,6 +21,7 @@ set(sources ethereum_tokens.c fsm.c home_sm.c + mayachain.c nano.c osmosis.c passphrase_sm.c diff --git a/lib/firmware/app_confirm.c b/lib/firmware/app_confirm.c index 80bad9fc..27f059a4 100644 --- a/lib/firmware/app_confirm.c +++ b/lib/firmware/app_confirm.c @@ -257,187 +257,187 @@ bool confirm_xpub(const char *node_str, const char *xpub) { node_str, "%s", xpub); } - /* - * confirm_cosmos_address() - Show cosmos address confirmation - * - * INPUT - * - desc: description to show with address - * - address: address to display both as string and in QR - * OUTPUT - * true/false of confirmation - * - */ - bool confirm_cosmos_address(const char *desc, const char *address) { - return confirm_with_custom_layout(&layout_cosmos_address_notification, - ButtonRequestType_ButtonRequest_Address, - desc, "%s", address); - } - - /* - * confirm_osmosis_address() - Show osmosis address confirmation - * - * INPUT - * - desc: description to show with address - * - address: address to display both as string and in QR - * OUTPUT - * true/false of confirmation - * - */ - bool confirm_osmosis_address(const char *desc, const char *address) { - return confirm_with_custom_layout(&layout_osmosis_address_notification, - ButtonRequestType_ButtonRequest_Address, - desc, "%s", address); - } +/* + * confirm_cosmos_address() - Show cosmos address confirmation + * + * INPUT + * - desc: description to show with address + * - address: address to display both as string and in QR + * OUTPUT + * true/false of confirmation + * + */ +bool confirm_cosmos_address(const char *desc, const char *address) { + return confirm_with_custom_layout(&layout_cosmos_address_notification, + ButtonRequestType_ButtonRequest_Address, + desc, "%s", address); +} - /* - * confirm_ethereum_address() - Show ethereum address confirmation - * - * INPUT - * - desc: description to show with address - * - address: address to display both as string and in QR - * OUTPUT - * true/false of confirmation - * - */ - bool confirm_ethereum_address(const char *desc, const char *address) { - return confirm_with_custom_layout(&layout_ethereum_address_notification, - ButtonRequestType_ButtonRequest_Address, - desc, "%s", address); - } +/* + * confirm_osmosis_address() - Show osmosis address confirmation + * + * INPUT + * - desc: description to show with address + * - address: address to display both as string and in QR + * OUTPUT + * true/false of confirmation + * + */ +bool confirm_osmosis_address(const char *desc, const char *address) { + return confirm_with_custom_layout(&layout_osmosis_address_notification, + ButtonRequestType_ButtonRequest_Address, + desc, "%s", address); +} - /* - * confirm_nano_address() - Show nano address confirmation - * - * INPUT - * - desc: description to show with address - * - address: address to display both as string and in QR - * OUTPUT - * true/false of confirmation - * - */ - bool confirm_nano_address(const char *desc, const char *address) { - return confirm_with_custom_layout(&layout_nano_address_notification, - ButtonRequestType_ButtonRequest_Address, - desc, "%s", address); - } +/* + * confirm_ethereum_address() - Show ethereum address confirmation + * + * INPUT + * - desc: description to show with address + * - address: address to display both as string and in QR + * OUTPUT + * true/false of confirmation + * + */ +bool confirm_ethereum_address(const char *desc, const char *address) { + return confirm_with_custom_layout(&layout_ethereum_address_notification, + ButtonRequestType_ButtonRequest_Address, + desc, "%s", address); +} - /* - * confirm_address() - Show address confirmation - * - * INPUT - * - desc: description to show with address - * - address: address to display both as string and in QR - * OUTPUT - * true/false of confirmation - * - */ - bool confirm_address(const char *desc, const char *address) { - return confirm_with_custom_layout(&layout_address_notification, - ButtonRequestType_ButtonRequest_Address, - desc, "%s", address); - } +/* + * confirm_nano_address() - Show nano address confirmation + * + * INPUT + * - desc: description to show with address + * - address: address to display both as string and in QR + * OUTPUT + * true/false of confirmation + * + */ +bool confirm_nano_address(const char *desc, const char *address) { + return confirm_with_custom_layout(&layout_nano_address_notification, + ButtonRequestType_ButtonRequest_Address, + desc, "%s", address); +} - /* - * confirm_sign_identity() - Show identity confirmation - * - * INPUT - * - identity: identity information from protocol buffer - * - challenge: challenge string - * OUTPUT - * true/false of confirmation - * - */ - bool confirm_sign_identity(const IdentityType *identity, - const char *challenge) { - char title[CONFIRM_SIGN_IDENTITY_TITLE], body[CONFIRM_SIGN_IDENTITY_BODY]; - - /* Format protocol */ - if (identity->has_proto && identity->proto[0]) { - strlcpy(title, identity->proto, sizeof(title)); - kk_strupr(title); - strlcat(title, " login to: ", sizeof(title)); - } else { - strlcpy(title, "Login to: ", sizeof(title)); - } +/* + * confirm_address() - Show address confirmation + * + * INPUT + * - desc: description to show with address + * - address: address to display both as string and in QR + * OUTPUT + * true/false of confirmation + * + */ +bool confirm_address(const char *desc, const char *address) { + return confirm_with_custom_layout(&layout_address_notification, + ButtonRequestType_ButtonRequest_Address, + desc, "%s", address); +} - /* Format host and port */ - if (identity->has_host && identity->host[0]) { - strlcpy(body, "host: ", sizeof(body)); - strlcat(body, identity->host, sizeof(body)); +/* + * confirm_sign_identity() - Show identity confirmation + * + * INPUT + * - identity: identity information from protocol buffer + * - challenge: challenge string + * OUTPUT + * true/false of confirmation + * + */ +bool confirm_sign_identity(const IdentityType *identity, + const char *challenge) { + char title[CONFIRM_SIGN_IDENTITY_TITLE], body[CONFIRM_SIGN_IDENTITY_BODY]; + + /* Format protocol */ + if (identity->has_proto && identity->proto[0]) { + strlcpy(title, identity->proto, sizeof(title)); + kk_strupr(title); + strlcat(title, " login to: ", sizeof(title)); + } else { + strlcpy(title, "Login to: ", sizeof(title)); + } - if (identity->has_port && identity->port[0]) { - strlcat(body, ":", sizeof(body)); - strlcat(body, identity->port, sizeof(body)); - } + /* Format host and port */ + if (identity->has_host && identity->host[0]) { + strlcpy(body, "host: ", sizeof(body)); + strlcat(body, identity->host, sizeof(body)); - strlcat(body, "\n", sizeof(body)); - } else { - body[0] = 0; + if (identity->has_port && identity->port[0]) { + strlcat(body, ":", sizeof(body)); + strlcat(body, identity->port, sizeof(body)); } - /* Format user */ - if (identity->has_user && identity->user[0]) { - strlcat(body, "user: ", sizeof(body)); - strlcat(body, identity->user, sizeof(body)); - strlcat(body, "\n", sizeof(body)); - } + strlcat(body, "\n", sizeof(body)); + } else { + body[0] = 0; + } - /* Format challenge */ - if (challenge && strlen(challenge) != 0) { - strlcat(body, challenge, sizeof(body)); - } + /* Format user */ + if (identity->has_user && identity->user[0]) { + strlcat(body, "user: ", sizeof(body)); + strlcat(body, identity->user, sizeof(body)); + strlcat(body, "\n", sizeof(body)); + } - return confirm(ButtonRequestType_ButtonRequest_SignIdentity, title, "%s", - body); + /* Format challenge */ + if (challenge && strlen(challenge) != 0) { + strlcat(body, challenge, sizeof(body)); } - bool confirm_omni(ButtonRequestType button_request, const char *title, - const uint8_t *data, uint32_t size) { - uint32_t tx_type, currency; - REVERSE32(*(const uint32_t *)(data + 4), tx_type); - if (tx_type == 0x00000000 && size == 20) { // OMNI simple send - char str_out[32]; - REVERSE32(*(const uint32_t *)(data + 8), currency); - const char *suffix = "UNKN"; - switch (currency) { - case 1: - suffix = " OMNI"; - break; - case 2: - suffix = " tOMNI"; - break; - case 3: - suffix = " MAID"; - break; - case 31: - suffix = " USDT"; - break; - } - uint64_t amount_be, amount; - memcpy(&amount_be, data + 12, sizeof(uint64_t)); - REVERSE64(amount_be, amount); - bn_format_uint64(amount, NULL, suffix, BITCOIN_DIVISIBILITY, 0, false, - str_out, sizeof(str_out)); - return confirm(button_request, title, _("Do you want to send %s?"), - str_out); - } else { - return confirm(button_request, title, _("Unknown Transaction")); + return confirm(ButtonRequestType_ButtonRequest_SignIdentity, title, "%s", + body); +} + +bool confirm_omni(ButtonRequestType button_request, const char *title, + const uint8_t *data, uint32_t size) { + uint32_t tx_type, currency; + REVERSE32(*(const uint32_t *)(data + 4), tx_type); + if (tx_type == 0x00000000 && size == 20) { // OMNI simple send + char str_out[32]; + REVERSE32(*(const uint32_t *)(data + 8), currency); + const char *suffix = "UNKN"; + switch (currency) { + case 1: + suffix = " OMNI"; + break; + case 2: + suffix = " tOMNI"; + break; + case 3: + suffix = " MAID"; + break; + case 31: + suffix = " USDT"; + break; } + uint64_t amount_be, amount; + memcpy(&amount_be, data + 12, sizeof(uint64_t)); + REVERSE64(amount_be, amount); + bn_format_uint64(amount, NULL, suffix, BITCOIN_DIVISIBILITY, 0, false, + str_out, sizeof(str_out)); + return confirm(button_request, title, _("Do you want to send %s?"), + str_out); + } else { + return confirm(button_request, title, _("Unknown Transaction")); } +} - bool confirm_data(ButtonRequestType button_request, const char *title, - const uint8_t *data, uint32_t size) { - const char *str = (const char *)data; - char hex[50 * 2 + 1]; - if (!is_valid_ascii(data, size)) { - if (size > 50) size = 50; - memset(hex, 0, sizeof(hex)); - data2hex(data, size, hex); - if (size > 50) { - hex[50 * 2 - 1] = '.'; - hex[50 * 2 - 2] = '.'; - } - str = hex; +bool confirm_data(ButtonRequestType button_request, const char *title, + const uint8_t *data, uint32_t size) { + const char *str = (const char *)data; + char hex[50 * 2 + 1]; + if (!is_valid_ascii(data, size)) { + if (size > 50) size = 50; + memset(hex, 0, sizeof(hex)); + data2hex(data, size, hex); + if (size > 50) { + hex[50 * 2 - 1] = '.'; + hex[50 * 2 - 2] = '.'; } - return confirm(button_request, title, "%s", str); + str = hex; } + return confirm(button_request, title, "%s", str); +} diff --git a/lib/firmware/coins.c b/lib/firmware/coins.c index 1e29c1cc..108d3bb1 100644 --- a/lib/firmware/coins.c +++ b/lib/firmware/coins.c @@ -409,6 +409,8 @@ bool isTendermint(const char *coin_name) { if (strcmp(coin_name, "Osmosis") == 0) return true; + if (strcmp(coin_name, "MAYAChain") == 0) return true; + if (strcmp(coin_name, "Binance") == 0) return true; if (strcmp(coin_name, "THORChain") == 0) return true; diff --git a/lib/firmware/fsm.c b/lib/firmware/fsm.c index cdd42a23..0d779f44 100644 --- a/lib/firmware/fsm.c +++ b/lib/firmware/fsm.c @@ -45,6 +45,7 @@ #include "keepkey/firmware/ethereum_tokens.h" #include "keepkey/firmware/fsm.h" #include "keepkey/firmware/home_sm.h" +#include "keepkey/firmware/mayachain.h" #include "keepkey/firmware/osmosis.h" #include "keepkey/firmware/passphrase_sm.h" #include "keepkey/firmware/pin_sm.h" @@ -82,6 +83,7 @@ #include "messages-nano.pb.h" #include "messages-ripple.pb.h" #include "messages-thorchain.pb.h" +#include "messages-mayachain.pb.h" #include @@ -244,8 +246,7 @@ void fsm_sendSuccess(const char *text) { msg_write(MessageType_MessageType_Success, resp); } -void fsm_sendFailure(FailureType code, const char *text) -{ +void fsm_sendFailure(FailureType code, const char *text) { if (reset_msg_stack) { fsm_msgInitialize((Initialize *)0); reset_msg_stack = false; @@ -282,3 +283,4 @@ void fsm_msgClearSession(ClearSession *msg) { #include "fsm_msg_ripple.h" #include "fsm_msg_tendermint.h" #include "fsm_msg_thorchain.h" +#include "fsm_msg_mayachain.h" diff --git a/lib/firmware/fsm_msg_mayachain.h b/lib/firmware/fsm_msg_mayachain.h new file mode 100644 index 00000000..8d76e5d4 --- /dev/null +++ b/lib/firmware/fsm_msg_mayachain.h @@ -0,0 +1,270 @@ + +void fsm_msgMayachainGetAddress(const MayachainGetAddress *msg) { + RESP_INIT(MayachainAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + const CoinType *coin = fsm_getCoin(true, "MAYAChain"); + if (!coin) { + return; + } + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + char mainnet[] = "maya"; + char testnet[] = "smaya"; + char *pfix; + + if (!node) { + return; + } + + hdnode_fill_public_key(node); + + pfix = mainnet; + if (msg->has_testnet && msg->testnet) { + pfix = testnet; + } + + if (!tendermint_getAddress(node, pfix, resp->address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Can't encode address")); + layoutHome(); + return; + } + + if (msg->has_show_display && msg->show_display) { + char node_str[NODE_STRING_LENGTH]; + if (!bip32_node_to_string(node_str, sizeof(node_str), coin, msg->address_n, + msg->address_n_count, /*whole_account=*/false, + /*show_addridx=*/false) && + !bip32_path_to_string(node_str, sizeof(node_str), msg->address_n, + msg->address_n_count)) { + memset(node_str, 0, sizeof(node_str)); + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Can't create Bip32 Path String")); + layoutHome(); + } + + bool mismatch = + tendermint_pathMismatched(coin, msg->address_n, msg->address_n_count); + if (mismatch) { + if (!confirm(ButtonRequestType_ButtonRequest_Other, "WARNING", + "Wrong address path for selected coin. Continue at your own " + "risk!")) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (!confirm_ethereum_address(node_str, resp->address)) { + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_ActionCancelled, + "Show address cancelled"); + layoutHome(); + return; + } + } + + resp->has_address = true; + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_MayachainAddress, resp); + layoutHome(); +} + +void fsm_msgMayachainSignTx(const MayachainSignTx *msg) { + CHECK_INITIALIZED + CHECK_PIN + + if (!msg->has_account_number || !msg->has_chain_id || !msg->has_fee_amount || + !msg->has_gas || !msg->has_sequence) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Missing Fields On Message"); + layoutHome(); + return; + } + + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) { + return; + } + + hdnode_fill_public_key(node); + + RESP_INIT(MayachainMsgRequest); + + if (!mayachain_signTxInit(node, msg)) { + mayachain_signAbort(); + memzero(node, sizeof(*node)); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Failed to initialize transaction signing")); + layoutHome(); + return; + } + + memzero(node, sizeof(*node)); + msg_write(MessageType_MessageType_MayachainMsgRequest, resp); + layoutHome(); +} + +void fsm_msgMayachainMsgAck(const MayachainMsgAck *msg) { + // Confirm transaction basics + // supports only 1 message ack + CHECK_PARAM(mayachain_signingIsInited(), "Signing not in progress"); + if (msg->has_send && msg->send.has_to_address && msg->send.has_amount) { + // pass + } else if (msg->has_deposit && msg->deposit.has_asset && + msg->deposit.has_amount && msg->deposit.has_memo && + msg->deposit.has_signer) { + // pass + } else { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Invalid MAYAChain Message Type")); + layoutHome(); + return; + } + + const CoinType *coin = fsm_getCoin(true, "MAYAChain"); + if (!coin) { + return; + } + + const MayachainSignTx *sign_tx = mayachain_getMayachainSignTx(); + + if (msg->has_send) { + switch (msg->send.address_type) { + case OutputAddressType_TRANSFER: + default: { + char amount_str[32]; + bn_format_uint64(msg->send.amount, NULL, " cacao", 10, 0, false, + amount_str, sizeof(amount_str)); + if (!confirm_transaction_output( + ButtonRequestType_ButtonRequest_ConfirmOutput, amount_str, + msg->send.to_address)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + break; + } + } + if (!mayachain_signTxUpdateMsgSend(msg->send.amount, + msg->send.to_address)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include send message in transaction"); + layoutHome(); + return; + } + + } else if (msg->has_deposit) { + char amount_str[32]; + char asset_str[21]; + asset_str[0] = ' '; + strlcpy(&(asset_str[1]), msg->deposit.asset, sizeof(asset_str) - 1); + bn_format_uint64(msg->deposit.amount, NULL, asset_str, 10, 0, false, + amount_str, sizeof(amount_str)); + if (!confirm_transaction_output( + ButtonRequestType_ButtonRequest_ConfirmOutput, amount_str, + msg->deposit.signer)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (msg->deposit.has_memo) { + // See if we can parse the memo + if (!mayachain_parseConfirmMemo(msg->deposit.memo, + sizeof(msg->deposit.memo))) { + // Memo not recognizable, ask to confirm it + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), + "%s", msg->deposit.memo)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + } + + if (!mayachain_signTxUpdateMsgDeposit(&(msg->deposit))) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include deposit message in transaction"); + layoutHome(); + return; + } + } + + if (!mayachain_signingIsFinished()) { + RESP_INIT(MayachainMsgRequest); + msg_write(MessageType_MessageType_MayachainMsgRequest, resp); + return; + } + + if (sign_tx->has_memo && !msg->deposit.has_memo) { + // See if we can parse the tx memo. This memo ignored if deposit msg has + // memo + if (!mayachain_parseConfirmMemo(sign_tx->memo, sizeof(sign_tx->memo))) { + // Memo not recognizable, ask to confirm it + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), "%s", + sign_tx->memo)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + } + + char node_str[NODE_STRING_LENGTH]; + if (!bip32_node_to_string(node_str, sizeof(node_str), coin, + sign_tx->address_n, sign_tx->address_n_count, + /*whole_account=*/false, + /*show_addridx=*/false) && + !bip32_path_to_string(node_str, sizeof(node_str), sign_tx->address_n, + sign_tx->address_n_count)) { + memset(node_str, 0, sizeof(node_str)); + } + + if (!confirm(ButtonRequestType_ButtonRequest_SignTx, node_str, + "Sign this CACAO transaction on %s? " + "Additional network fees apply.", + sign_tx->chain_id)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + RESP_INIT(MayachainSignedTx); + + if (!mayachain_signTxFinalize(resp->public_key.bytes, + resp->signature.bytes)) { + mayachain_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to finalize signature"); + layoutHome(); + return; + } + + resp->public_key.size = 33; + resp->has_public_key = true; + resp->signature.size = 64; + resp->has_signature = true; + mayachain_signAbort(); + layoutHome(); + msg_write(MessageType_MessageType_MayachainSignedTx, resp); +} diff --git a/lib/firmware/mayachain.c b/lib/firmware/mayachain.c new file mode 100644 index 00000000..669fdfd2 --- /dev/null +++ b/lib/firmware/mayachain.c @@ -0,0 +1,325 @@ +/* + * This file is part of the Keepkey project. + * + * Copyright (C) 2021 Shapeshift + * + * 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 "keepkey/firmware/mayachain.h" +#include "keepkey/board/confirm_sm.h" +#include "keepkey/board/util.h" +#include "keepkey/firmware/home_sm.h" +#include "keepkey/firmware/storage.h" +#include "keepkey/firmware/tendermint.h" +#include "trezor/crypto/secp256k1.h" +#include "trezor/crypto/ecdsa.h" +#include "trezor/crypto/memzero.h" +#include "trezor/crypto/segwit_addr.h" + +#include +#include + +static CONFIDENTIAL HDNode node; +static SHA256_CTX ctx; +static bool initialized; +static uint32_t msgs_remaining; +static MayachainSignTx msg; +static bool testnet; + +const MayachainSignTx *mayachain_getMayachainSignTx(void) { return &msg; } + +bool mayachain_signTxInit(const HDNode *_node, const MayachainSignTx *_msg) { + initialized = true; + msgs_remaining = _msg->msg_count; + testnet = false; + + if (_msg->has_testnet) { + testnet = _msg->testnet; + } + + memzero(&node, sizeof(node)); + memcpy(&node, _node, sizeof(node)); + memcpy(&msg, _msg, sizeof(msg)); + + bool success = true; + char buffer[64 + 1]; + + sha256_Init(&ctx); + + // Each segment guaranteed to be less than or equal to 64 bytes + // 19 + ^20 + 1 = ^40 + if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "{\"account_number\":\"%" PRIu64 "\"", + msg.account_number)) + return false; + + // + const char *const chainid_prefix = ",\"chain_id\":\""; + sha256_Update(&ctx, (uint8_t *)chainid_prefix, strlen(chainid_prefix)); + tendermint_sha256UpdateEscaped(&ctx, msg.chain_id, strlen(msg.chain_id)); + + // 30 + ^10 + 19 = ^59 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\",\"fee\":{\"amount\":[{\"amount\":\"%" PRIu32 + "\",\"denom\":\"cacao\"}]", + msg.fee_amount); + + // 8 + ^10 + 2 = ^20 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"gas\":\"%" PRIu32 "\"}", msg.gas); + + // + const char *const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + if (msg.has_memo) { + tendermint_sha256UpdateEscaped(&ctx, msg.memo, strlen(msg.memo)); + } + + // 10 + sha256_Update(&ctx, (uint8_t *)"\",\"msgs\":[", 10); + + return success; +} + +bool mayachain_signTxUpdateMsgSend(const uint64_t amount, + const char *to_address) { + char mainnetp[] = "maya"; + char testnetp[] = "smaya"; + char *pfix; + char buffer[64 + 1]; + + size_t decoded_len; + char hrp[45]; + uint8_t decoded[38]; + if (!bech32_decode(hrp, decoded, &decoded_len, to_address)) { + return false; + } + + char from_address[46]; + + pfix = mainnetp; + if (testnet) { + pfix = testnetp; + } + + if (!tendermint_getAddress(&node, pfix, from_address)) { + return false; + } + + bool success = true; + + const char *const prelude = "{\"type\":\"mayachain/MsgSend\",\"value\":{"; + sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + + // 21 + ^20 + 19 = ^60 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"amount\":[{\"amount\":\"%" PRIu64 "\",\"denom\":\"cacao\"}]", amount); + + // 17 + 45 + 1 = 63 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"from_address\":\"%s\"", from_address); + + // 15 + 45 + 3 = 63 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"to_address\":\"%s\"}}", to_address); + + msgs_remaining--; + return success; +} + +bool mayachain_signTxUpdateMsgDeposit(const MayachainMsgDeposit *depmsg) { + char buffer[64 + 1]; + + bool success = true; + + const char *const prelude = "{\"type\":\"mayachain/MsgDeposit\",\"value\":{"; + sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + + // 20 + ^20 + 1 = ^41 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"coins\":[{\"amount\":\"%" PRIu64 "\"", depmsg->amount); + + // 10 + ^20 + 3 = ^33 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + ",\"asset\":\"%s\"}]", depmsg->asset); + + // + const char *const memo_prefix = ",\"memo\":\""; + sha256_Update(&ctx, (uint8_t *)memo_prefix, strlen(memo_prefix)); + tendermint_sha256UpdateEscaped(&ctx, depmsg->memo, strlen(depmsg->memo)); + + // 17 + 45 + 1 = 63 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\",\"signer\":\"%s\"}}", depmsg->signer); + + msgs_remaining--; + return success; +} + +bool mayachain_signTxFinalize(uint8_t *public_key, uint8_t *signature) { + char buffer[64 + 1]; + + // 16 + ^20 = ^36 + if (!tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "],\"sequence\":\"%" PRIu64 "\"}", msg.sequence)) + return false; + + hdnode_fill_public_key(&node); + memcpy(public_key, node.public_key, 33); + + uint8_t hash[SHA256_DIGEST_LENGTH]; + sha256_Final(&ctx, hash); + return ecdsa_sign_digest(&secp256k1, node.private_key, hash, signature, NULL, + NULL) == 0; +} + +bool mayachain_signingIsInited(void) { return initialized; } + +bool mayachain_signingIsFinished(void) { return msgs_remaining == 0; } + +void mayachain_signAbort(void) { + initialized = false; + msgs_remaining = 0; + memzero(&msg, sizeof(msg)); + memzero(&node, sizeof(node)); +} + +bool mayachain_parseConfirmMemo(const char *swapStr, size_t size) { + /* + Input: swapStr is candidate mayachain data + size is the size of swapStr (<= 256) + Memos should be of the form: + transaction:chain.ticker-id:destination:limit + ^^^^^^^^^^^^^^----------asset + + So, swap USDT to dest address 0x41e55..., limit 420 + SWAP:ETH.USDT-0xdac17f958d2ee523a2206206994597c13d831ec7:0x41e5560054824ea6b0732e656e3ad64e20e94e45:420 + + Swap transactions can be indicated by "SWAP" or "s" or "=" + */ + + char *parseTokPtrs[7] = {NULL, NULL, NULL, NULL, + NULL, NULL, NULL}; // we can parse up to 7 tokens + char *tok; + char memoBuf[256]; + uint16_t ctr; + + // check if memo data is recognized + + if (size > sizeof(memoBuf)) return false; + memzero(memoBuf, sizeof(memoBuf)); + strlcpy(memoBuf, swapStr, size); + memoBuf[255] = '\0'; // ensure null termination + tok = strtok(memoBuf, ":"); + + // get transaction and asset + for (ctr = 0; ctr < 3; ctr++) { + if (tok != NULL) { + parseTokPtrs[ctr] = tok; + tok = strtok(NULL, ":."); + } else { + break; + } + } + + if (ctr != 3) { + // Must have three tokens at this point: transaction, chain, asset. If + // not, just confirm data + return false; + } + + // Check for swap + if (strncmp(parseTokPtrs[0], "SWAP", 4) == 0 || *parseTokPtrs[0] == 's' || + *parseTokPtrs[0] == '=') { + // This is a swap, set up destination and limit + // This is the dest, may be blank which means swap to self + parseTokPtrs[3] = "self"; + parseTokPtrs[4] = "none"; + if (tok != NULL) { + if ((uint32_t)(tok - (parseTokPtrs[2] + strlen(parseTokPtrs[2]))) == 1) { + // has dest address + parseTokPtrs[3] = tok; + tok = strtok(NULL, ":"); + } + if (tok != NULL) { + // has limit + parseTokPtrs[4] = tok; + } + } + + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Mayachain swap", "Confirm swap asset %s\n on chain %s", parseTokPtrs[2], parseTokPtrs[1])) { + return false; + } + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Mayachain swap", "Confirm to %s", parseTokPtrs[3])) { + return false; + } + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Mayachain swap", "Confirm limit %s", parseTokPtrs[4])) { + return false; + } + return true; + } + + // Check for add liquidity + else if (strncmp(parseTokPtrs[0], "ADD", 3) == 0 || *parseTokPtrs[0] == 'a' || + *parseTokPtrs[0] == '+') { + if (tok != NULL) { + // add liquidity pool address + parseTokPtrs[3] = tok; + } + + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Mayachain add liquidity", "Confirm add asset %s\n on chain %s pool", + parseTokPtrs[2], parseTokPtrs[1])) { + return false; + } + if (tok != NULL) { + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Mayachain add liquidity", "Confirm to %s", parseTokPtrs[3])) { + return false; + } + } + return true; + } + + // Check for withdraw liquidity + else if (strncmp(parseTokPtrs[0], "WITHDRAW", 8) == 0 || strncmp(parseTokPtrs[0], "wd", 2) == 0 || + *parseTokPtrs[0] == '-') { + if (tok != NULL) { + // add liquidity pool address + parseTokPtrs[3] = tok; + } else { + return false; // malformed memo + } + + float percent = (float)(atoi(parseTokPtrs[3])) / 100; + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmOutput, + "Mayachain withdraw liquidity", "Confirm withdraw %3.2f%% of asset %s on chain %s", + percent, parseTokPtrs[2], parseTokPtrs[1])) { + return false; + } + return true; + + } else { + // Just confirm whatever coin data if no mayachain intention data parsable + return false; + } +} diff --git a/lib/firmware/messagemap.def b/lib/firmware/messagemap.def index 6097a49e..e8e37438 100644 --- a/lib/firmware/messagemap.def +++ b/lib/firmware/messagemap.def @@ -69,6 +69,10 @@ MSG_IN(MessageType_MessageType_ThorchainSignTx, ThorchainSignTx, fsm_msgThorchainSignTx) MSG_IN(MessageType_MessageType_ThorchainMsgAck, ThorchainMsgAck, fsm_msgThorchainMsgAck) + MSG_IN(MessageType_MessageType_MayachainGetAddress, MayachainGetAddress, fsm_msgMayachainGetAddress) + MSG_IN(MessageType_MessageType_MayachainSignTx, MayachainSignTx, fsm_msgMayachainSignTx) + MSG_IN(MessageType_MessageType_MayachainMsgAck, MayachainMsgAck, fsm_msgMayachainMsgAck) + /* Normal Out Messages */ MSG_OUT(MessageType_MessageType_Success, Success, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_Failure, Failure, NO_PROCESS_FUNC) @@ -124,6 +128,10 @@ MSG_OUT(MessageType_MessageType_ThorchainMsgRequest, ThorchainMsgRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_ThorchainSignedTx, ThorchainSignedTx, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_MayachainAddress, MayachainAddress, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_MayachainMsgRequest, MayachainMsgRequest, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_MayachainSignedTx, MayachainSignedTx, NO_PROCESS_FUNC) + #if DEBUG_LINK /* Debug Messages */ DEBUG_IN(MessageType_MessageType_DebugLinkDecision, DebugLinkDecision, NO_PROCESS_FUNC) diff --git a/lib/transport/CMakeLists.txt b/lib/transport/CMakeLists.txt index 22ddebd0..41b8d586 100644 --- a/lib/transport/CMakeLists.txt +++ b/lib/transport/CMakeLists.txt @@ -14,6 +14,7 @@ set(protoc_pb_sources ${DEVICE_PROTOCOL}/messages-ripple.proto ${DEVICE_PROTOCOL}/messages-tendermint.proto ${DEVICE_PROTOCOL}/messages-thorchain.proto + ${DEVICE_PROTOCOL}/messages-mayachain.proto ${DEVICE_PROTOCOL}/messages.proto) set(protoc_pb_options @@ -27,6 +28,7 @@ set(protoc_pb_options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-ripple.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-tendermint.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-thorchain.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-mayachain.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages.options) set(protoc_c_sources @@ -40,6 +42,7 @@ set(protoc_c_sources ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages.pb.c) set(protoc_c_headers @@ -53,6 +56,7 @@ set(protoc_c_headers ${CMAKE_BINARY_DIR}/include/messages-ripple.pb.h ${CMAKE_BINARY_DIR}/include/messages-tendermint.pb.h ${CMAKE_BINARY_DIR}/include/messages-thorchain.pb.h + ${CMAKE_BINARY_DIR}/include/messages-mayachain.pb.h ${CMAKE_BINARY_DIR}/include/messages.pb.h) set(protoc_pb_sources_moved @@ -66,6 +70,7 @@ set(protoc_pb_sources_moved ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-mayachain.proto ${CMAKE_BINARY_DIR}/lib/transport/messages.proto) add_custom_command( @@ -127,6 +132,10 @@ add_custom_command( ${PROTOC_BINARY} -I. -I/usr/include --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb "--nanopb_out=-f messages-thorchain.options:." messages-thorchain.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb + "--nanopb_out=-f messages-mayachain.options:." messages-mayachain.proto COMMAND ${PROTOC_BINARY} -I. -I/usr/include --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb diff --git a/unittests/firmware/coins.cpp b/unittests/firmware/coins.cpp index baa86f37..c11a59bd 100644 --- a/unittests/firmware/coins.cpp +++ b/unittests/firmware/coins.cpp @@ -173,7 +173,12 @@ TEST(Coins, BIP32AccountName) { {0x80000000 | 44, 0x80000000 | 931, 0x80000000 | 69, 0, 0}, 5, true, - "THORChain Account #69"}}; + "THORChain Account #69"}, + {"MAYAChain", + {0x80000000 | 44, 0x80000000 | 931, 0x80000000 | 69, 0, 0}, + 5, + true, + "MAYAChain Account #69"}}; for (const auto &vec : vector) { char node_str[NODE_STRING_LENGTH]; diff --git a/unittests/firmware/mayachain.cpp b/unittests/firmware/mayachain.cpp new file mode 100644 index 00000000..8a319a61 --- /dev/null +++ b/unittests/firmware/mayachain.cpp @@ -0,0 +1,74 @@ +extern "C" { +#include "keepkey/firmware/coins.h" +#include "keepkey/firmware/mayachain.h" +#include "keepkey/firmware/tendermint.h" +#include "trezor/crypto/secp256k1.h" +} + +#include "gtest/gtest.h" +#include + +TEST(Mayachain, MayachainGetAddress) { + HDNode node = { + 0, + 0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0x03, 0x15, 0x19, 0x71, 0x3b, 0x8b, 0x42, 0xbd, 0xc3, 0x67, 0x11, + 0x2d, 0x33, 0x13, 0x2c, 0xf1, 0x4c, 0xed, 0xf9, 0x28, 0xac, 0x57, + 0x71, 0xd4, 0x44, 0xba, 0x45, 0x9b, 0x94, 0x97, 0x11, 0x7b, 0xa3}, + &secp256k1_info}; + char addr[46]; + ASSERT_TRUE(tendermint_getAddress(&node, "maya", addr)); + EXPECT_EQ(std::string("maya1ls33ayg26kmltw7jjy55p32ghjna09zp7z4etj"), addr); +} + +TEST(Mayachain, MayachainSignTx) { + HDNode node = { + 0, + 0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0xb9, 0x9a, 0x39, 0x3a, 0x5a, 0x53, 0x0d, 0x90, 0xef, 0x6e, 0x46, + 0x4e, 0x8e, 0x2f, 0x2b, 0x8b, 0x5c, 0x64, 0xa7, 0x97, 0x29, 0xcd, + 0x60, 0x3b, 0x1f, 0xba, 0x33, 0x81, 0x7d, 0x1a, 0x75, 0xa1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + &secp256k1_info}; + hdnode_fill_public_key(&node); + + const MayachainSignTx msg = { + 5, {0x80000000 | 44, 0x80000000 | 931, 0x80000000, 0, 0}, // address_n + true, 6359, // account_number + true, "mayachain-mainnet-v1", // chain_id + true, 3000, // fee_amount + true, 200000, // gas + true, "", // memo + true, 19, // sequence + true, 1 // msg_count + }; + ASSERT_TRUE(mayachain_signTxInit(&node, &msg)); + + ASSERT_TRUE(mayachain_signTxUpdateMsgSend( + 100, "maya1g9el7lzjwh9yun2c4jjzhy09j98vkhfxfqkl5k")); + + uint8_t public_key[33]; + uint8_t signature[64]; + + ASSERT_TRUE(mayachain_signTxFinalize(public_key, signature)); + + EXPECT_TRUE( + memcmp(signature, + (uint8_t *)"\x8a\x91\x43\x54\xca\xe7\x45\x30\x0e\xfb\x88\xee\xdd" + "\xac\xc0\xb5\xa3\x3d\x18\xb1\xe6\x54\x26\x70\x8f\x93" + "\x69\x67\xd5\x21\x84\xbb\x6b\x58\x3d\xe3\x21\xd0\x3e" + "\x26\xb2\xd8\x00\x7d\x81\x84\x34\x82\x5a\xfa\xa2\x80" + "\x54\x88\x90\xc6\xec\xf0\x3b\xf5\x33\x0f\x3e\x9a", + 64) == 0); +} \ No newline at end of file diff --git a/unittests/firmware/thorchain.cpp b/unittests/firmware/thorchain.cpp index 2da0ae8f..4fa8faa8 100644 --- a/unittests/firmware/thorchain.cpp +++ b/unittests/firmware/thorchain.cpp @@ -45,13 +45,13 @@ TEST(Thorchain, ThorchainSignTx) { const ThorchainSignTx msg = { 5, {0x80000000 | 44, 0x80000000 | 931, 0x80000000, 0, 0}, // address_n - true, 0, // account_number + true, 0, // account_number true, "thorchain", // chain_id - true, 5000, // fee_amount - true, 200000, // gas - true, "", // memo - true, 0, // sequence - true, 1 // msg_count + true, 5000, // fee_amount + true, 200000, // gas + true, "", // memo + true, 0, // sequence + true, 1 // msg_count }; ASSERT_TRUE(thorchain_signTxInit(&node, &msg)); @@ -60,6 +60,7 @@ TEST(Thorchain, ThorchainSignTx) { uint8_t public_key[33]; uint8_t signature[64]; + ASSERT_TRUE(thorchain_signTxFinalize(public_key, signature)); EXPECT_TRUE(