2014-04-29 12:26:51 +00:00
|
|
|
/*
|
2019-06-17 18:27:55 +00:00
|
|
|
* This file is part of the Trezor project, https://trezor.io/
|
2014-04-29 12:26:51 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
|
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2017-10-03 11:11:53 +00:00
|
|
|
#include <libopencm3/stm32/flash.h>
|
|
|
|
|
2017-01-02 20:22:59 +00:00
|
|
|
#include "address.h"
|
2018-01-13 14:20:10 +00:00
|
|
|
#include "aes/aes.h"
|
2014-11-15 01:01:21 +00:00
|
|
|
#include "base58.h"
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "bip32.h"
|
2014-12-08 18:58:13 +00:00
|
|
|
#include "bip39.h"
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "coins.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "crypto.h"
|
2016-04-22 15:49:00 +00:00
|
|
|
#include "curves.h"
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "debug.h"
|
|
|
|
#include "ecdsa.h"
|
|
|
|
#include "fsm.h"
|
2022-05-03 12:52:18 +00:00
|
|
|
#include "fw_signatures.h"
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "gettext.h"
|
|
|
|
#include "hmac.h"
|
|
|
|
#include "layout2.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "memzero.h"
|
|
|
|
#include "messages.h"
|
|
|
|
#include "messages.pb.h"
|
|
|
|
#include "oled.h"
|
|
|
|
#include "pinmatrix.h"
|
|
|
|
#include "protect.h"
|
|
|
|
#include "recovery.h"
|
|
|
|
#include "reset.h"
|
|
|
|
#include "rng.h"
|
|
|
|
#include "secp256k1.h"
|
|
|
|
#include "signing.h"
|
|
|
|
#include "supervise.h"
|
|
|
|
#include "transaction.h"
|
|
|
|
#include "trezor.h"
|
|
|
|
#include "usb.h"
|
|
|
|
#include "util.h"
|
2014-04-29 12:26:51 +00:00
|
|
|
|
2019-04-27 14:15:35 +00:00
|
|
|
#if !BITCOIN_ONLY
|
|
|
|
#include "ethereum.h"
|
|
|
|
#include "nem.h"
|
|
|
|
#include "nem2.h"
|
|
|
|
#include "stellar.h"
|
|
|
|
#endif
|
|
|
|
|
2021-02-25 13:05:23 +00:00
|
|
|
#if EMULATOR
|
|
|
|
#include <stdio.h>
|
|
|
|
#endif
|
|
|
|
|
2014-04-29 12:26:51 +00:00
|
|
|
// message methods
|
|
|
|
|
2021-02-10 19:26:55 +00:00
|
|
|
static uint8_t msg_resp[MSG_OUT_DECODED_SIZE] __attribute__((aligned));
|
2019-03-29 16:10:31 +00:00
|
|
|
|
2023-01-02 19:10:23 +00:00
|
|
|
// Authorization message type triggered by DoPreauthorized.
|
|
|
|
static MessageType authorization_type = 0;
|
|
|
|
|
2022-12-27 15:37:38 +00:00
|
|
|
static uint32_t unlock_path = 0;
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
#define RESP_INIT(TYPE) \
|
|
|
|
TYPE *resp = (TYPE *)(void *)msg_resp; \
|
|
|
|
_Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \
|
|
|
|
memzero(resp, sizeof(TYPE));
|
|
|
|
|
|
|
|
#define CHECK_INITIALIZED \
|
|
|
|
if (!config_isInitialized()) { \
|
|
|
|
fsm_sendFailure(FailureType_Failure_NotInitialized, NULL); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CHECK_NOT_INITIALIZED \
|
|
|
|
if (config_isInitialized()) { \
|
|
|
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, \
|
|
|
|
_("Device is already initialized. Use Wipe first.")); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CHECK_PIN \
|
|
|
|
if (!protectPin(true)) { \
|
|
|
|
layoutHome(); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CHECK_PIN_UNCACHED \
|
|
|
|
if (!protectPin(false)) { \
|
|
|
|
layoutHome(); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
2022-02-17 09:44:40 +00:00
|
|
|
#define CHECK_UNLOCKED \
|
|
|
|
if (!session_isUnlocked()) { \
|
|
|
|
layoutHome(); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
#define CHECK_PARAM(cond, errormsg) \
|
|
|
|
if (!(cond)) { \
|
|
|
|
fsm_sendFailure(FailureType_Failure_DataError, (errormsg)); \
|
|
|
|
layoutHome(); \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
void fsm_sendSuccess(const char *text) {
|
|
|
|
RESP_INIT(Success);
|
|
|
|
if (text) {
|
|
|
|
resp->has_message = true;
|
|
|
|
strlcpy(resp->message, text, sizeof(resp->message));
|
|
|
|
}
|
|
|
|
msg_write(MessageType_MessageType_Success, resp);
|
2014-04-29 12:26:51 +00:00
|
|
|
}
|
|
|
|
|
2017-12-11 18:15:31 +00:00
|
|
|
#if DEBUG_LINK
|
2019-03-29 16:10:31 +00:00
|
|
|
void fsm_sendFailureDebug(FailureType code, const char *text,
|
|
|
|
const char *source)
|
2017-12-11 18:15:31 +00:00
|
|
|
#else
|
2018-08-27 17:06:11 +00:00
|
|
|
void fsm_sendFailure(FailureType code, const char *text)
|
2017-12-11 18:15:31 +00:00
|
|
|
#endif
|
2014-04-29 12:26:51 +00:00
|
|
|
{
|
2019-03-29 16:10:31 +00:00
|
|
|
if (protectAbortedByCancel) {
|
|
|
|
protectAbortedByCancel = false;
|
|
|
|
}
|
|
|
|
if (protectAbortedByInitialize) {
|
|
|
|
fsm_msgInitialize((Initialize *)0);
|
|
|
|
protectAbortedByInitialize = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RESP_INIT(Failure);
|
|
|
|
resp->has_code = true;
|
|
|
|
resp->code = code;
|
|
|
|
if (!text) {
|
|
|
|
switch (code) {
|
|
|
|
case FailureType_Failure_UnexpectedMessage:
|
|
|
|
text = _("Unexpected message");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_ButtonExpected:
|
|
|
|
text = _("Button expected");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_DataError:
|
|
|
|
text = _("Data error");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_ActionCancelled:
|
|
|
|
text = _("Action cancelled by user");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_PinExpected:
|
|
|
|
text = _("PIN expected");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_PinCancelled:
|
|
|
|
text = _("PIN cancelled");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_PinInvalid:
|
|
|
|
text = _("PIN invalid");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_InvalidSignature:
|
|
|
|
text = _("Invalid signature");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_ProcessError:
|
|
|
|
text = _("Process error");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_NotEnoughFunds:
|
|
|
|
text = _("Not enough funds");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_NotInitialized:
|
|
|
|
text = _("Device not initialized");
|
|
|
|
break;
|
|
|
|
case FailureType_Failure_PinMismatch:
|
|
|
|
text = _("PIN mismatch");
|
|
|
|
break;
|
2019-10-23 17:20:08 +00:00
|
|
|
case FailureType_Failure_WipeCodeMismatch:
|
|
|
|
text = _("Wipe code mismatch");
|
|
|
|
break;
|
2020-08-28 08:45:01 +00:00
|
|
|
case FailureType_Failure_InvalidSession:
|
|
|
|
text = _("Invalid session");
|
|
|
|
break;
|
2019-03-29 16:10:31 +00:00
|
|
|
case FailureType_Failure_FirmwareError:
|
|
|
|
text = _("Firmware error");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-12-11 18:15:31 +00:00
|
|
|
#if DEBUG_LINK
|
2019-03-29 16:10:31 +00:00
|
|
|
resp->has_message = true;
|
|
|
|
strlcpy(resp->message, source, sizeof(resp->message));
|
|
|
|
if (text) {
|
|
|
|
strlcat(resp->message, text, sizeof(resp->message));
|
|
|
|
}
|
2017-12-11 18:15:31 +00:00
|
|
|
#else
|
2019-03-29 16:10:31 +00:00
|
|
|
if (text) {
|
|
|
|
resp->has_message = true;
|
|
|
|
strlcpy(resp->message, text, sizeof(resp->message));
|
|
|
|
}
|
2017-12-11 18:15:31 +00:00
|
|
|
#endif
|
2019-03-29 16:10:31 +00:00
|
|
|
msg_write(MessageType_MessageType_Failure, resp);
|
2014-04-29 12:26:51 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
static const CoinInfo *fsm_getCoin(bool has_name, const char *name) {
|
2019-09-30 15:34:38 +00:00
|
|
|
const CoinInfo *coin = NULL;
|
2019-03-29 16:10:31 +00:00
|
|
|
if (has_name) {
|
|
|
|
coin = coinByName(name);
|
|
|
|
} else {
|
|
|
|
coin = coinByName("Bitcoin");
|
|
|
|
}
|
|
|
|
if (!coin) {
|
|
|
|
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid coin name"));
|
|
|
|
layoutHome();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return coin;
|
2015-01-26 11:51:56 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n,
|
|
|
|
size_t address_n_count,
|
|
|
|
uint32_t *fingerprint) {
|
|
|
|
static CONFIDENTIAL HDNode node;
|
|
|
|
if (fingerprint) {
|
|
|
|
*fingerprint = 0;
|
|
|
|
}
|
2020-01-20 14:00:07 +00:00
|
|
|
if (!config_getRootNode(&node, curve)) {
|
2019-03-29 16:10:31 +00:00
|
|
|
layoutHome();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!address_n || address_n_count == 0) {
|
|
|
|
return &node;
|
|
|
|
}
|
|
|
|
if (hdnode_private_ckd_cached(&node, address_n, address_n_count,
|
|
|
|
fingerprint) == 0) {
|
|
|
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
|
|
|
_("Failed to derive private key"));
|
|
|
|
layoutHome();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return &node;
|
2014-04-29 12:26:51 +00:00
|
|
|
}
|
|
|
|
|
2022-12-15 11:42:36 +00:00
|
|
|
static bool fsm_getSlip21Key(const char *path[], size_t path_count,
|
|
|
|
uint8_t key[32]) {
|
|
|
|
const uint8_t *seed = config_getSeed();
|
|
|
|
if (seed == NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CONFIDENTIAL Slip21Node node;
|
|
|
|
slip21_from_seed(seed, 64, &node);
|
|
|
|
for (size_t i = 0; i < path_count; ++i) {
|
|
|
|
slip21_derive_path(&node, (uint8_t *)path[i], strlen(path[i]));
|
|
|
|
}
|
|
|
|
memcpy(key, slip21_key(&node), 32);
|
|
|
|
memzero(&node, sizeof(node));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
static bool fsm_layoutAddress(const char *address, const char *desc,
|
|
|
|
bool ignorecase, size_t prefixlen,
|
|
|
|
const uint32_t *address_n, size_t address_n_count,
|
2019-10-23 17:03:31 +00:00
|
|
|
bool address_is_account,
|
|
|
|
const MultisigRedeemScriptType *multisig,
|
2021-01-13 22:41:13 +00:00
|
|
|
int multisig_index, uint32_t multisig_xpub_magic,
|
|
|
|
const CoinInfo *coin) {
|
2019-10-23 17:03:31 +00:00
|
|
|
int screen = 0, screens = 2;
|
|
|
|
if (multisig) {
|
2020-02-24 17:00:46 +00:00
|
|
|
screens += 2 * cryptoMultisigPubkeyCount(multisig);
|
2019-10-23 17:03:31 +00:00
|
|
|
}
|
2019-03-29 16:10:31 +00:00
|
|
|
for (;;) {
|
2019-10-23 17:03:31 +00:00
|
|
|
switch (screen) {
|
|
|
|
case 0: { // show address
|
|
|
|
const char *display_addr = address;
|
|
|
|
// strip cashaddr prefix
|
|
|
|
if (prefixlen) {
|
|
|
|
display_addr += prefixlen;
|
|
|
|
}
|
|
|
|
layoutAddress(display_addr, desc, false, ignorecase, address_n,
|
|
|
|
address_n_count, address_is_account);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: { // show QR code
|
|
|
|
layoutAddress(address, desc, true, ignorecase, address_n,
|
|
|
|
address_n_count, address_is_account);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: { // show XPUBs
|
2020-02-24 17:00:46 +00:00
|
|
|
int index = (screen - 2) / 2;
|
|
|
|
int page = (screen - 2) % 2;
|
2020-10-13 08:50:12 +00:00
|
|
|
char xpub[XPUB_MAXLEN] = {0};
|
2019-10-23 17:03:31 +00:00
|
|
|
const HDNodeType *node_ptr = NULL;
|
|
|
|
if (multisig->nodes_count) { // use multisig->nodes
|
|
|
|
node_ptr = &(multisig->nodes[index]);
|
|
|
|
} else if (multisig->pubkeys_count) { // use multisig->pubkeys
|
|
|
|
node_ptr = &(multisig->pubkeys[index].node);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!node_ptr) {
|
|
|
|
strlcat(xpub, "ERROR", sizeof(xpub));
|
|
|
|
} else {
|
|
|
|
HDNode node;
|
|
|
|
if (!hdnode_from_xpub(node_ptr->depth, node_ptr->child_num,
|
|
|
|
node_ptr->chain_code.bytes,
|
|
|
|
node_ptr->public_key.bytes, coin->curve_name,
|
|
|
|
&node)) {
|
|
|
|
strlcat(xpub, "ERROR", sizeof(xpub));
|
|
|
|
} else {
|
|
|
|
hdnode_serialize_public(&node, node_ptr->fingerprint,
|
2021-01-13 22:41:13 +00:00
|
|
|
multisig_xpub_magic, xpub, sizeof(xpub));
|
2019-10-23 17:03:31 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-21 10:18:31 +00:00
|
|
|
layoutXPUBMultisig(xpub, index, page, multisig_index == index);
|
2019-10-23 17:03:31 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-03-29 16:10:31 +00:00
|
|
|
}
|
|
|
|
if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (protectAbortedByCancel || protectAbortedByInitialize) {
|
|
|
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
|
|
|
layoutHome();
|
|
|
|
return false;
|
|
|
|
}
|
2019-10-23 17:03:31 +00:00
|
|
|
screen = (screen + 1) % screens;
|
2019-03-29 16:10:31 +00:00
|
|
|
}
|
2018-01-06 15:47:32 +00:00
|
|
|
}
|
|
|
|
|
2021-11-07 20:02:49 +00:00
|
|
|
static bool fsm_layoutPaginated(const char *description, const uint8_t *msg,
|
|
|
|
uint32_t len, bool is_ascii) {
|
|
|
|
const char **str = NULL;
|
2021-11-09 09:06:56 +00:00
|
|
|
const uint32_t row_len = is_ascii ? 18 : 8;
|
2021-11-07 20:02:49 +00:00
|
|
|
do {
|
|
|
|
const uint32_t show_len = MIN(len, row_len * 4);
|
|
|
|
if (is_ascii) {
|
|
|
|
str = split_message(msg, show_len, row_len);
|
|
|
|
} else {
|
|
|
|
str = split_message_hex(msg, show_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
msg += show_len;
|
|
|
|
len -= show_len;
|
|
|
|
|
|
|
|
const char *label = len > 0 ? _("Next") : _("Confirm");
|
2021-11-09 09:06:56 +00:00
|
|
|
layoutDialogSwipeEx(&bmp_icon_question, _("Cancel"), label, description,
|
|
|
|
str[0], str[1], str[2], str[3], NULL, NULL, FONT_FIXED);
|
2021-11-07 20:02:49 +00:00
|
|
|
|
|
|
|
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} while (len > 0);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fsm_layoutSignMessage(const uint8_t *msg, uint32_t len) {
|
|
|
|
if (is_valid_ascii(msg, len)) {
|
|
|
|
return fsm_layoutPaginated(_("Sign message?"), msg, len, true);
|
|
|
|
} else {
|
|
|
|
return fsm_layoutPaginated(_("Sign binary message?"), msg, len, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fsm_layoutVerifyMessage(const uint8_t *msg, uint32_t len) {
|
|
|
|
if (is_valid_ascii(msg, len)) {
|
|
|
|
return fsm_layoutPaginated(_("Verified message?"), msg, len, true);
|
|
|
|
} else {
|
|
|
|
return fsm_layoutPaginated(_("Verified binary message?"), msg, len, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-16 09:38:08 +00:00
|
|
|
bool fsm_layoutCommitmentData(const uint8_t *msg, uint32_t len) {
|
|
|
|
if (is_valid_ascii(msg, len)) {
|
|
|
|
return fsm_layoutPaginated(_("Commitment data"), msg, len, true);
|
|
|
|
} else {
|
|
|
|
return fsm_layoutPaginated(_("Binary commitment data"), msg, len, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-25 11:54:29 +00:00
|
|
|
void fsm_msgRebootToBootloader(void) {
|
2021-04-15 12:25:36 +00:00
|
|
|
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
|
|
|
_("Do you want to"), _("restart device in"),
|
|
|
|
_("bootloader mode?"), NULL, NULL, NULL);
|
|
|
|
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
|
|
|
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
|
|
|
layoutHome();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
oledClear();
|
|
|
|
oledRefresh();
|
|
|
|
fsm_sendSuccess(_("Rebooting"));
|
|
|
|
// make sure the outgoing message is sent
|
2021-12-06 21:08:20 +00:00
|
|
|
usbFlush(500);
|
2021-02-25 11:54:29 +00:00
|
|
|
#if !EMULATOR
|
|
|
|
svc_reboot_to_bootloader();
|
2021-02-25 13:05:23 +00:00
|
|
|
#else
|
|
|
|
printf("Reboot!\n");
|
2021-02-25 11:54:29 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-02-17 16:16:42 +00:00
|
|
|
void fsm_abortWorkflows(void) {
|
|
|
|
recovery_abort();
|
|
|
|
signing_abort();
|
2023-01-02 19:10:23 +00:00
|
|
|
authorization_type = 0;
|
2022-12-27 15:37:38 +00:00
|
|
|
unlock_path = 0;
|
2022-02-17 16:16:42 +00:00
|
|
|
#if !BITCOIN_ONLY
|
|
|
|
ethereum_signing_abort();
|
|
|
|
stellar_signingAbort();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-01-02 19:25:37 +00:00
|
|
|
void fsm_postMsgCleanup(MessageType message_type) {
|
|
|
|
if (message_type != MessageType_MessageType_DoPreauthorized) {
|
|
|
|
authorization_type = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message_type != MessageType_MessageType_UnlockPath) {
|
|
|
|
unlock_path = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 17:45:45 +00:00
|
|
|
bool fsm_layoutPathWarning(void) {
|
|
|
|
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL,
|
|
|
|
_("Wrong address path"), _("for selected coin."), NULL,
|
|
|
|
_("Continue at your"), _("own risk!"), NULL);
|
|
|
|
if (!protectButton(ButtonRequestType_ButtonRequest_UnknownDerivationPath,
|
|
|
|
false)) {
|
|
|
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-05-03 14:47:37 +00:00
|
|
|
#include "fsm_msg_coin.h"
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "fsm_msg_common.h"
|
2018-05-03 14:47:37 +00:00
|
|
|
#include "fsm_msg_crypto.h"
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "fsm_msg_debug.h"
|
2019-04-27 14:15:35 +00:00
|
|
|
|
|
|
|
#if !BITCOIN_ONLY
|
|
|
|
|
2019-03-29 16:10:31 +00:00
|
|
|
#include "fsm_msg_ethereum.h"
|
2018-05-03 14:47:37 +00:00
|
|
|
#include "fsm_msg_nem.h"
|
2018-08-27 15:20:29 +00:00
|
|
|
#include "fsm_msg_stellar.h"
|
2019-04-27 14:15:35 +00:00
|
|
|
|
|
|
|
#endif
|