diff --git a/CMakeLists.txt b/CMakeLists.txt index 3580fed88..465ec8205 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,9 @@ cmake_minimum_required(VERSION 3.7.2) -project(KeepKeyFirmware - - VERSION 7.6.1 - - LANGUAGES C CXX ASM) +project( + KeepKeyFirmware + VERSION 7.7.0 + LANGUAGES C CXX ASM) set(BOOTLOADER_MAJOR_VERSION 2) set(BOOTLOADER_MINOR_VERSION 1) @@ -13,10 +12,18 @@ set(BOOTLOADER_PATCH_VERSION 5) option(KK_EMULATOR "Build the emulator" OFF) option(KK_DEBUG_LINK "Build with debug-link enabled" OFF) option(KK_BUILD_FUZZERS "Build the fuzzers?" OFF) -set(LIBOPENCM3_PATH /root/libopencm3 CACHE PATH "Path to an already-built libopencm3") -set(PROTOC_BINARY protoc CACHE PATH "Path to the protobuf compiler binary") -set(NANOPB_DIR /root/nanopb CACHE PATH "Path to the nanopb build") -set(DEVICE_PROTOCOL ${CMAKE_SOURCE_DIR}/deps/device-protocol CACHE PATH "Path to device-protocol") +set(LIBOPENCM3_PATH + /root/libopencm3 + CACHE PATH "Path to an already-built libopencm3") +set(PROTOC_BINARY + protoc + CACHE PATH "Path to the protobuf compiler binary") +set(NANOPB_DIR + /root/nanopb + CACHE PATH "Path to the nanopb build") +set(DEVICE_PROTOCOL + ${CMAKE_SOURCE_DIR}/deps/device-protocol + CACHE PATH "Path to device-protocol") set(CMAKE_DEBUG_POSTFIX CACHE STRING "Debug library name postfix") list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/") @@ -24,24 +31,35 @@ include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) if(NOT EXISTS ${DEVICE_PROTOCOL}) - message(FATAL_ERROR "Missing deps/device-protocol symlink?") + message(FATAL_ERROR "Missing deps/device-protocol symlink?") endif() if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/googletest/CMakeLists.txt) - message(FATAL_ERROR "googletest missing. Need to 'git submodule update --init --recursive") + message( + FATAL_ERROR + "googletest missing. Need to 'git submodule update --init --recursive") endif() if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/crypto/trezor-firmware/crypto/Makefile) - message(FATAL_ERROR " trezor-crypto missing. Need to 'git submodule update --init --recursive") + message( + FATAL_ERROR + " trezor-crypto missing. Need to 'git submodule update --init --recursive" + ) endif() if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/qrenc/QR-Code-generator/c/Makefile) - message(FATAL_ERROR " QR-Code-generator missing. Need to 'git submodule update --init --recursive") + message( + FATAL_ERROR + " QR-Code-generator missing. Need to 'git submodule update --init --recursive" + ) endif() find_program(NANOPB_GENERATOR nanopb_generator.py) if(${KK_EMULATOR} AND NOT NANOPB_GENERATOR) - message(FATAL_ERROR "Must install nanopb 0.3.9.4, and put nanopb-nanopb-0.3.9.4/generator on your PATH") + message( + FATAL_ERROR + "Must install nanopb 0.3.9.4, and put nanopb-nanopb-0.3.9.4/generator on your PATH" + ) endif() if(${KK_EMULATOR}) @@ -95,18 +113,20 @@ endif() if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") add_definitions(-DDEBUG_ON) add_definitions(-DMEMORY_PROTECT=0) -elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR - "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel" OR - "${CMAKE_BUILD_TYPE}" STREQUAL "") +elseif( + "${CMAKE_BUILD_TYPE}" STREQUAL "Release" + OR "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel" + OR "${CMAKE_BUILD_TYPE}" STREQUAL "") add_definitions(-DNDEBUG) add_definitions(-DMEMORY_PROTECT=1) if(NOT ${KK_EMULATOR}) - message(WARNING - "*********************************************************************\n" - "* You are about to build a release version of KeepKey firmware. The *\n" - "* resulting bootloader image will memory protect the flash on your *\n" - "* device, so please use it with extreme care. *\n" - "*********************************************************************") + message( + WARNING + "*********************************************************************\n" + "* You are about to build a release version of KeepKey firmware. The *\n" + "* resulting bootloader image will memory protect the flash on your *\n" + "* device, so please use it with extreme care. *\n" + "*********************************************************************") endif() else() message(ERROR "Must pick Release *or* Debug CMAKE_BUILD_TYPE") @@ -129,8 +149,8 @@ if(NOT ${KK_EMULATOR}) add_library(ssp_nonshared ${CMAKE_BINARY_DIR}/ssp.c) set_property(TARGET ssp PROPERTY LINKER_LANGUAGE CXX) set_property(TARGET ssp_nonshared PROPERTY LINKER_LANGUAGE CXX) - set_target_properties(ssp ssp_nonshared - PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set_target_properties(ssp ssp_nonshared PROPERTIES LIBRARY_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/lib) endif() add_subdirectory(lib) @@ -151,9 +171,13 @@ if(${KK_EMULATOR}) add_test(test-board ${CMAKE_BINARY_DIR}/bin/board-unit) add_test(test-crypto ${CMAKE_BINARY_DIR}/bin/crypto-unit) - add_custom_target(xunit - COMMAND ${CMAKE_BINARY_DIR}/bin/firmware-unit --gtest_output=xml:${CMAKE_BINARY_DIR}/unittests/firmware.xml - COMMAND ${CMAKE_BINARY_DIR}/bin/board-unit --gtest_output=xml:${CMAKE_BINARY_DIR}/unittests/board.xml - COMMAND ${CMAKE_BINARY_DIR}/bin/crypto-unit --gtest_output=xml:${CMAKE_BINARY_DIR}/unittests/crypto.xml) + add_custom_target( + xunit + COMMAND ${CMAKE_BINARY_DIR}/bin/firmware-unit + --gtest_output=xml:${CMAKE_BINARY_DIR}/unittests/firmware.xml + COMMAND ${CMAKE_BINARY_DIR}/bin/board-unit + --gtest_output=xml:${CMAKE_BINARY_DIR}/unittests/board.xml + COMMAND ${CMAKE_BINARY_DIR}/bin/crypto-unit + --gtest_output=xml:${CMAKE_BINARY_DIR}/unittests/crypto.xml) endif() diff --git a/deps/device-protocol b/deps/device-protocol index 82ec74705..22ca1d983 160000 --- a/deps/device-protocol +++ b/deps/device-protocol @@ -1 +1 @@ -Subproject commit 82ec74705e5798580b532fac02fbbe5f5740f1e2 +Subproject commit 22ca1d983c3f513f338e2a0ba997427659646523 diff --git a/deps/python-keepkey b/deps/python-keepkey index b7af582e9..1e32069bd 160000 --- a/deps/python-keepkey +++ b/deps/python-keepkey @@ -1 +1 @@ -Subproject commit b7af582e9a380ad5b440a533734857728966ac85 +Subproject commit 1e32069bd8e6f38d1d5a95c2fb17e23f1712a6df diff --git a/include/keepkey/board/util.h b/include/keepkey/board/util.h index dd8b51ec7..17ccdc3c0 100644 --- a/include/keepkey/board/util.h +++ b/include/keepkey/board/util.h @@ -54,4 +54,8 @@ void dec64_to_str(uint64_t dec64_val, char *str); bool is_valid_ascii(const uint8_t *data, uint32_t size); +int base_to_precision(uint8_t *dest, const uint8_t *value, + const uint8_t dest_len, const uint8_t value_len, + const uint8_t precision); + #endif diff --git a/include/keepkey/firmware/app_confirm.h b/include/keepkey/firmware/app_confirm.h index 5185d2ea6..906e8debc 100644 --- a/include/keepkey/firmware/app_confirm.h +++ b/include/keepkey/firmware/app_confirm.h @@ -47,6 +47,7 @@ bool confirm_address(const char *desc, const char *address); bool confirm_xpub(const char *node_str, const char *xpub); bool confirm_sign_identity(const IdentityType *identity, const char *challenge); bool confirm_cosmos_address(const char *desc, const char *address); +bool confirm_osmosis_address(const char *desc, const char *address); bool confirm_ethereum_address(const char *desc, const char *address); bool confirm_nano_address(const char *desc, const char *address); bool confirm_omni(ButtonRequestType button_request, const char *title, diff --git a/include/keepkey/firmware/app_layout.h b/include/keepkey/firmware/app_layout.h index 7ea9997c6..8c7eb17e7 100644 --- a/include/keepkey/firmware/app_layout.h +++ b/include/keepkey/firmware/app_layout.h @@ -39,19 +39,19 @@ #define NO_TITLE_WIDTH 250 /* PIN Matrix */ -#define MATRIX_MASK_COLOR 0x00 -#define MATRIX_MASK_MARGIN 3 -#define PIN_MATRIX_GRID_SIZE 18 -#define PIN_MATRIX_ANIMATION_FREQUENCY_MS 40 -#define PIN_MATRIX_BACKGROUND 0x11 -#define PIN_MATRIX_STEP1 0x11 -#define PIN_MATRIX_STEP2 0x33 -#define PIN_MATRIX_STEP3 0x77 -#define PIN_MATRIX_STEP4 0xBB -#define PIN_MATRIX_FOREGROUND 0xFF -#define PIN_SLIDE_DELAY 20 -#define PIN_MAX_ANIMATION_MS 1000 -#define PIN_LEFT_MARGIN 195 +#define MATRIX_MASK_COLOR 0x00 +#define MATRIX_MASK_MARGIN 3 +#define PIN_MATRIX_GRID_SIZE 18 +#define PIN_MATRIX_ANIMATION_FREQUENCY_MS 40 +#define PIN_MATRIX_BACKGROUND 0x11 +#define PIN_MATRIX_STEP1 0x11 +#define PIN_MATRIX_STEP2 0x33 +#define PIN_MATRIX_STEP3 0x77 +#define PIN_MATRIX_STEP4 0xBB +#define PIN_MATRIX_FOREGROUND 0xFF +#define PIN_SLIDE_DELAY 20 +#define PIN_MAX_ANIMATION_MS 1000 +#define PIN_LEFT_MARGIN 195 /* Recovery Cypher */ #define CIPHER_ROWS 2 @@ -111,7 +111,9 @@ void layout_xpub_notification(const char *desc, const char *xpub, void layout_address_notification(const char *desc, const char *address, NotificationType type); void layout_cosmos_address_notification(const char *desc, const char *address, - NotificationType type); + NotificationType type); +void layout_osmosis_address_notification(const char *desc, const char *address, + NotificationType type); void layout_ethereum_address_notification(const char *desc, const char *address, NotificationType type); void layout_nano_address_notification(const char *desc, const char *address, diff --git a/include/keepkey/firmware/coins.def b/include/keepkey/firmware/coins.def index e4ec3b005..1817b6bde 100644 --- a/include/keepkey/firmware/coins.def +++ b/include/keepkey/firmware/coins.def @@ -39,10 +39,12 @@ X(true, "LTC Testnet", true, "tLTC", true, 48, true, 1000000, true, 5 X(true, ETHEREUM_TST, true, "tETH", true, NA, true, 100000, true, NA, true, "Ethereum Signed Message:\n", true, 0x80000001, true, 1, true, 18, false, NO_CONTRACT, false, 0, true, false, true, false, true, SECP256K1_STRING, false, "", false, "", false, false, false, 0, false, 0, false, "" ) X(true, "Binance", true, "BEP2", false, NA, false, NA, false, NA, false, {0}, true, 0x800002ca, false, 0, true, 8, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", true, "bnb", false, false, false, 0, false, 0, false, "" ) X(true, "Cosmos", true, "ATOM", false, NA, false, NA, false, NA, false, {0}, true, 0x80000076, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "cosmos", false, false, false, 0, false, 0, false, "" ) +X(true, "Osmosis", true, "OSMO", false, NA, false, NA, false, NA, false, {0}, true, 0x80000076, false, 0, true, 6, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "osmo", false, false, false, 0, false, 0, false, "" ) X(true, "Ripple", true, "Ripple",false, 0, false, 0, false, 0, false, {0}, true, 0x80000090, false, 0, false, 0, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "", false, false, true, 77429938, true, 78792518, false, "" ) X(true, "THORChain", true, "RUNE", false, NA, false, NA, false, NA, false, {0}, true, 0x800003a3, false, 0, true, 8, false, NO_CONTRACT, false, 0, false, false, false, false, true, SECP256K1_STRING, false, "", false, "thor", false, false, false, 0, false, 0, false, "" ) 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, "" ) + #undef X #undef NO_CONTRACT diff --git a/include/keepkey/firmware/fsm.h b/include/keepkey/firmware/fsm.h index cb5a5b7f2..0aaafcc46 100644 --- a/include/keepkey/firmware/fsm.h +++ b/include/keepkey/firmware/fsm.h @@ -106,6 +106,10 @@ void fsm_msgCosmosGetAddress(const CosmosGetAddress *msg); void fsm_msgCosmosSignTx(const CosmosSignTx *msg); void fsm_msgCosmosMsgAck(const CosmosMsgAck *msg); +void fsm_msgOsmosisGetAddress(const OsmosisGetAddress *msg); +void fsm_msgOsmosisSignTx(const OsmosisSignTx *msg); +void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg); + void fsm_msgThorchainGetAddress(const ThorchainGetAddress *msg); void fsm_msgThorchainSignTx(const ThorchainSignTx *msg); void fsm_msgThorchainMsgAck(const ThorchainMsgAck *msg); diff --git a/include/keepkey/firmware/osmosis.h b/include/keepkey/firmware/osmosis.h new file mode 100644 index 000000000..9a3f39498 --- /dev/null +++ b/include/keepkey/firmware/osmosis.h @@ -0,0 +1,75 @@ +#ifndef KEEPKEY_FIRMWARE_OSMOSIS_H +#define KEEPKEY_FIRMWARE_OSMOSIS_H + +#include "messages.pb.h" +#include "trezor/crypto/bip32.h" + +#include +#include + +typedef struct _OsmosisSignTx OsmosisSignTx; +typedef struct _OsmosisMsgLPAdd OsmosisMsgLPAdd; +typedef struct _OsmosisMsgLPRemove OsmosisMsgLPRemove; +typedef struct _OsmosisMsgSwap OsmosisMsgSwap; + +void debug_intermediate_hash(void); + +bool osmosis_signTxInit(const HDNode *_node, const OsmosisSignTx *_msg); + +bool osmosis_signTxUpdateMsgSend(const char *amount, const char *to_address); + +bool osmosis_signTxUpdateMsgDelegate(const char *amount, + const char *delegator_address, + const char *validator_address, + const char *denom); + +bool osmosis_signTxUpdateMsgUndelegate(const char *amount, + const char *delegator_address, + const char *validator_address, + const char *denom); + +bool osmosis_signTxUpdateMsgRedelegate(const char *amount, + const char *delegator_address, + const char *validator_src_address, + const char *validator_dst_address, + const char *denom); + +bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char *sender, + const char *share_out_amount, + const char *amount_in_max_a, + const char *denom_in_max_a, + const char *amount_in_max_b, + const char *denom_in_max_b); + +bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char *sender, + const char *share_out_amount, + const char *amount_out_min_a, + const char *denom_out_min_a, + const char *amount_out_min_b, + const char *denom_out_min_b); + +bool osmosis_signTxUpdateMsgRewards(const char *delegator_address, + const char *validator_address); + +bool osmosis_signTxUpdateMsgIBCTransfer(const char *amount, const char *sender, + const char *receiver, + const char *source_channel, + const char *source_port, + const char *revision_number, + const char *revision_height, + const char *denom); + +bool osmosis_signTxUpdateMsgSwap(const uint64_t pool_id, + const char *token_out_denom, + const char *sender, + const char *token_in_amount, + const char *token_in_denom, + const char *token_out_min_amount); + +bool osmosis_signTxFinalize(uint8_t *public_key, uint8_t *signature); +bool osmosis_signingIsInited(void); +bool osmosis_signingIsFinished(void); +void osmosis_signAbort(void); +const OsmosisSignTx *osmosis_getOsmosisSignTx(void); + +#endif diff --git a/include/keepkey/firmware/signtx_tendermint.h b/include/keepkey/firmware/signtx_tendermint.h index f3f5ad892..8933e2d02 100644 --- a/include/keepkey/firmware/signtx_tendermint.h +++ b/include/keepkey/firmware/signtx_tendermint.h @@ -13,14 +13,12 @@ bool tendermint_signTxInit(const HDNode *_node, const void *_msg, const size_t msgsize, const char *denom); bool tendermint_signTxUpdateMsgSend(const uint64_t amount, const char *to_address, - const char *chainstr, - const char *denom, + const char *chainstr, const char *denom, const char *msgTypePrefix); bool tendermint_signTxUpdateMsgDelegate(const uint64_t amount, const char *delegator_address, const char *validator_address, - const char *chainstr, - const char *denom, + const char *chainstr, const char *denom, const char *msgTypePrefix); bool tendermint_signTxUpdateMsgUndelegate(const uint64_t amount, const char *delegator_address, @@ -35,8 +33,7 @@ bool tendermint_signTxUpdateMsgRedelegate( bool tendermint_signTxUpdateMsgRewards(const uint64_t *amount, const char *delegator_address, const char *validator_address, - const char *chainstr, - const char *denom, + const char *chainstr, const char *denom, const char *msgTypePrefix); bool tendermint_signTxUpdateMsgIBCTransfer( const uint64_t amount, const char *sender, const char *receiver, @@ -49,4 +46,4 @@ bool tendermint_signingIsFinished(void); void tendermint_signAbort(void); const void *tendermint_getSignTx(void); -#endif +#endif \ No newline at end of file diff --git a/include/keepkey/transport/interface.h b/include/keepkey/transport/interface.h index d87b5a1fa..1a3b7e34f 100644 --- a/include/keepkey/transport/interface.h +++ b/include/keepkey/transport/interface.h @@ -29,6 +29,7 @@ #include "messages-ethereum.pb.h" #include "messages-binance.pb.h" #include "messages-cosmos.pb.h" +#include "messages-osmosis.pb.h" #include "messages-eos.pb.h" #include "messages-ripple.pb.h" #include "messages-tendermint.pb.h" diff --git a/include/keepkey/transport/messages-osmosis.options b/include/keepkey/transport/messages-osmosis.options new file mode 100644 index 000000000..6da6dfeb8 --- /dev/null +++ b/include/keepkey/transport/messages-osmosis.options @@ -0,0 +1,71 @@ +OsmosisGetAddress.address_n max_count:10 + +OsmosisAddress.address max_size:53 + +OsmosisSignTx.address_n max_count:10 +OsmosisSignTx.chain_id max_size:32 +OsmosisSignTx.memo max_size:256 + +OsmosisMsgSend.from_address max_size:53 +OsmosisMsgSend.to_address max_size:53 +OsmosisMsgSend.denom max_size:69 +OsmosisMsgSend.amount max_size:33 + +OsmosisMsgDelegate.delegator_address max_size:53 +OsmosisMsgDelegate.validator_address max_size:53 +OsmosisMsgDelegate.denom max_size:69 +OsmosisMsgDelegate.amount max_size:33 + +OsmosisMsgUndelegate.delegator_address max_size:53 +OsmosisMsgUndelegate.validator_address max_size:53 +OsmosisMsgUndelegate.denom max_size:69 +OsmosisMsgUndelegate.amount max_size:33 + +OsmosisMsgRedelegate.delegator_address max_size:53 +OsmosisMsgRedelegate.validator_src_address max_size:53 +OsmosisMsgRedelegate.validator_dst_address max_size:53 +OsmosisMsgRedelegate.denom max_size:69 +OsmosisMsgRedelegate.amount max_size:33 +OsmosisMsgRedelegate.amount max_size:33 + +OsmosisMsgRewards.delegator_address max_size:53 +OsmosisMsgRewards.validator_address max_size:53 + +OsmosisMsgLPAdd.sender max_size:53 +OsmosisMsgLPAdd.denom_in_max_a max_size:69 +OsmosisMsgLPAdd.denom_in_max_b max_size:69 +OsmosisMsgLPAdd.amount_in_max_a max_size:33 +OsmosisMsgLPAdd.amount_in_max_b max_size:33 +OsmosisMsgLPAdd.share_out_amount max_size:33 + +OsmosisMsgLPRemove.sender max_size:53 +OsmosisMsgLPRemove.denom_out_min_a max_size:69 +OsmosisMsgLPRemove.denom_out_min_b max_size:69 +OsmosisMsgLPRemove.amount_out_min_a max_size:33 +OsmosisMsgLPRemove.amount_out_min_b max_size:33 +OsmosisMsgLPRemove.share_in_amount max_size:33 + +OsmosisMsgLPStake.owner max_size:53 +OsmosisMsgLPStake.denom max_size:69 +OsmosisMsgLPStake.amount max_size:33 + +OsmosisMsgLPUnstake.owner max_size:53 +OsmosisMsgLPUnstake.id max_size:129 + +OsmosisMsgIBCTransfer.sender max_size:53 +OsmosisMsgIBCTransfer.receiver max_size:53 +OsmosisMsgIBCTransfer.source_channel max_size:32 +OsmosisMsgIBCTransfer.source_port max_size:32 +OsmosisMsgIBCTransfer.revision_height max_size:16 +OsmosisMsgIBCTransfer.revision_number max_size:9 +OsmosisMsgIBCTransfer.denom max_size:69 +OsmosisMsgIBCTransfer.amount max_size:33 + +OsmosisMsgSwap.sender max_size:53 +OsmosisMsgSwap.token_out_denom max_size:69 +OsmosisMsgSwap.token_in_denom max_size:69 +OsmosisMsgSwap.token_in_amount max_size:33 +OsmosisMsgSwap.token_out_min_amount max_size:33 + +OsmosisSignedTx.public_key max_size:33 +OsmosisSignedTx.signature max_size:64 \ No newline at end of file diff --git a/lib/board/util.c b/lib/board/util.c index ab9a58901..065f53edb 100644 --- a/lib/board/util.c +++ b/lib/board/util.c @@ -102,3 +102,35 @@ bool is_valid_ascii(const uint8_t *data, uint32_t size) { } return true; } + +/* convert number in base units to specified decimal precision */ +int base_to_precision(uint8_t *dest, const uint8_t *value, + const uint8_t dest_len, const uint8_t value_len, + const uint8_t precision) { + if (!(dest && value)) { + // invalid pointer + return -1; + } + if (value_len + 1 > dest_len) { + // value too large for output buffer + return -1; + } + memset(dest, '0', dest_len); + uint8_t leading_digits = + ((value_len - precision) > 0) ? (value_len - precision) : 0; + + if (!leading_digits) { + memcpy(dest, "0.", 2); + uint8_t offset = + 2 + (((precision - value_len) > 0) ? (precision - value_len) : 0); + strlcpy((char *)&dest[offset], (char *)value, value_len); + } else { + uint8_t copy_len = MIN((value_len - leading_digits), precision); + memcpy(dest, value, leading_digits); + dest[leading_digits] = '.'; + strlcpy((char *)&dest[leading_digits + 1], (char *)&value[leading_digits], + copy_len); + } + dest[dest_len] = '\0'; + return 0; +} diff --git a/lib/firmware/CMakeLists.txt b/lib/firmware/CMakeLists.txt index edb553a39..ee916b721 100644 --- a/lib/firmware/CMakeLists.txt +++ b/lib/firmware/CMakeLists.txt @@ -22,6 +22,7 @@ set(sources fsm.c home_sm.c nano.c + osmosis.c passphrase_sm.c pin_sm.c policy.c diff --git a/lib/firmware/app_confirm.c b/lib/firmware/app_confirm.c index 78de26d49..80bad9fca 100644 --- a/lib/firmware/app_confirm.c +++ b/lib/firmware/app_confirm.c @@ -273,6 +273,22 @@ bool confirm_xpub(const char *node_str, const char *xpub) { 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_ethereum_address() - Show ethereum address confirmation * diff --git a/lib/firmware/app_layout.c b/lib/firmware/app_layout.c index 382263bf1..6f431d74f 100644 --- a/lib/firmware/app_layout.c +++ b/lib/firmware/app_layout.c @@ -473,6 +473,48 @@ void layout_cosmos_address_notification(const char *desc, const char *address, layout_notification_icon(type, &sp); } +/* + * layout_osmosis_address_notification() - Display osmosis address + * notification + * + * INPUT + * - desc: description of address being shown (normal or multisig) + * - address: osmosis address to display + * - type: notification type + * OUTPUT + * none + */ +void layout_osmosis_address_notification(const char *desc, const char *address, + NotificationType type) { + DrawableParams sp; + const Font *address_font = get_body_font(); + ; + Canvas *canvas = layout_get_canvas(); + + call_leaving_handler(); + layout_clear(); + + if (strcmp(desc, "") != 0) { + const Font *title_font = get_title_font(); + sp.y = TOP_MARGIN_FOR_TWO_LINES; + sp.x = LEFT_MARGIN + 65; + sp.color = BODY_COLOR; + draw_string(canvas, title_font, desc, &sp, TRANSACTION_WIDTH - 2, + font_height(title_font) + BODY_FONT_LINE_PADDING); + } + + /* Body */ + sp.y = TOP_MARGIN_FOR_TWO_LINES + TOP_MARGIN + TOP_MARGIN; + sp.x = LEFT_MARGIN + 65; + sp.color = BODY_COLOR; + + draw_string(canvas, address_font, address, &sp, 160, + font_height(address_font) + BODY_FONT_LINE_PADDING); + + layout_address(address, QR_LARGE); + layout_notification_icon(type, &sp); +} + /* * layout_ethereum_address_notification() - Display ethereum address * notification diff --git a/lib/firmware/coins.c b/lib/firmware/coins.c index d71a2817f..1e29c1cc0 100644 --- a/lib/firmware/coins.c +++ b/lib/firmware/coins.c @@ -407,6 +407,8 @@ static const char *account_prefix(const CoinType *coin, bool isTendermint(const char *coin_name) { if (strcmp(coin_name, "Cosmos") == 0) return true; + if (strcmp(coin_name, "Osmosis") == 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 a214dad5c..cdd42a23e 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/osmosis.h" #include "keepkey/firmware/passphrase_sm.h" #include "keepkey/firmware/pin_sm.h" #include "keepkey/firmware/policy.h" @@ -76,6 +77,7 @@ #include "messages-ethereum.pb.h" #include "messages-binance.pb.h" #include "messages-cosmos.pb.h" +#include "messages-osmosis.pb.h" #include "messages-eos.pb.h" #include "messages-nano.pb.h" #include "messages-ripple.pb.h" @@ -275,6 +277,7 @@ void fsm_msgClearSession(ClearSession *msg) { #include "fsm_msg_debug.h" #include "fsm_msg_eos.h" #include "fsm_msg_cosmos.h" +#include "fsm_msg_osmosis.h" #include "fsm_msg_binance.h" #include "fsm_msg_ripple.h" #include "fsm_msg_tendermint.h" diff --git a/lib/firmware/fsm_msg_osmosis.h b/lib/firmware/fsm_msg_osmosis.h new file mode 100644 index 000000000..a84ae925b --- /dev/null +++ b/lib/firmware/fsm_msg_osmosis.h @@ -0,0 +1,695 @@ +#include +#define OSMOSIS_PRECISION 6 +#define OSMOSIS_LP_ASSET_PRECISION 18 + +void fsm_msgOsmosisGetAddress(const OsmosisGetAddress *msg) { + RESP_INIT(OsmosisAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + const CoinType *coin = fsm_getCoin(true, "Osmosis"); + if (!coin) { + return; + } + HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + char mainnet[] = "osmo"; + char testnet[] = "tosmo"; + 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_OsmosisAddress, resp); + layoutHome(); +} + +void fsm_msgOsmosisSignTx(const OsmosisSignTx *msg) { + CHECK_INITIALIZED + CHECK_PIN + + if (!msg->has_account_number || !msg->has_chain_id || !msg->has_fee_amount || + !msg->has_gas || !msg->has_sequence) { + osmosis_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(OsmosisMsgRequest); + + if (!osmosis_signTxInit(node, msg)) { + osmosis_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_OsmosisMsgRequest, resp); + layoutHome(); +} + +void fsm_msgOsmosisMsgAck(const OsmosisMsgAck *msg) { + // Confirm transaction basics + CHECK_PARAM(osmosis_signingIsInited(), "Signing not in progress"); + + const CoinType *coin = fsm_getCoin(true, "Osmosis"); + if (!coin) { + return; + } + + const OsmosisSignTx *sign_tx = osmosis_getOsmosisSignTx(); + + if (msg->has_send) { + if (!msg->send.has_to_address || !msg->send.has_amount) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + + char amount_str[40]; + sprintf(amount_str, "%.6f OSMO", + atof(msg->send.amount) / pow(10, OSMOSIS_PRECISION)); + if (!confirm_transaction_output( + ButtonRequestType_ButtonRequest_ConfirmOutput, amount_str, + msg->send.to_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgSend(msg->send.amount, msg->send.to_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include send message in transaction"); + layoutHome(); + return; + } + + } else if (msg->has_delegate) { + /** Confirm required transaction parameters exist */ + if (!msg->delegate.has_delegator_address || + !msg->delegate.has_validator_address || !msg->delegate.has_amount) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + /** Confirm transaction parameters on-screen */ + if (!confirm_osmosis_address("Confirm Delegator Address", + msg->delegate.delegator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Confirm Validator Address", + msg->delegate.validator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Amount", + "%.6f OSMO", + atof(msg->delegate.amount) / pow(10, OSMOSIS_PRECISION))) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgDelegate( + msg->delegate.amount, msg->delegate.delegator_address, + msg->delegate.validator_address, msg->delegate.denom)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include delegate message in transaction"); + layoutHome(); + return; + } + } else if (msg->has_undelegate) { + /** Confirm required transaction parameters exist */ + if (!msg->undelegate.has_delegator_address || + !msg->undelegate.has_validator_address || !msg->undelegate.has_amount) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + /** Confirm transaction parameters on-screen */ + + if (!confirm_osmosis_address("Confirm Delegator Address", + msg->undelegate.delegator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Confirm Validator Address", + msg->undelegate.validator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Amount", + "%.6f OSMO", + atof(msg->undelegate.amount) / pow(10, OSMOSIS_PRECISION))) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgUndelegate( + msg->undelegate.amount, msg->undelegate.delegator_address, + msg->undelegate.validator_address, msg->undelegate.denom)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include undelegate message in transaction"); + layoutHome(); + return; + } + } else if (msg->has_lp_add) { + /** Confirm required transaction parameters exist */ + if (!msg->lp_add.has_sender || !msg->lp_add.has_pool_id || + !msg->lp_add.has_share_out_amount || !msg->lp_add.has_denom_in_max_a || + !msg->lp_add.has_amount_in_max_a || !msg->lp_add.has_denom_in_max_b || + !msg->lp_add.has_amount_in_max_b) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + + /** Confirm transaction parameters on-screen */ + char insoamt[33] = {0}; + uint8_t outsoamt[34] = {0}; + strlcpy(insoamt, msg->lp_add.share_out_amount, + sizeof(msg->lp_add.share_out_amount)); + + if (base_to_precision(outsoamt, (uint8_t *)insoamt, sizeof(outsoamt), + strlen(insoamt), OSMOSIS_LP_ASSET_PRECISION) < 0) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_Other, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Add Liquidity", + "Deposit %.6f %s and...", + atof(msg->lp_add.amount_in_max_b) / pow(10, OSMOSIS_PRECISION), + (!strcmp(msg->lp_add.denom_in_max_b, "uosmo")) + ? "OSMO" + : msg->lp_add.denom_in_max_b)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Add Liquidity", + "... %.6f %s?", + atof(msg->lp_add.amount_in_max_a) / pow(10, OSMOSIS_PRECISION), + (!strcmp(msg->lp_add.denom_in_max_a, "uosmo")) + ? "OSMO" + : msg->lp_add.denom_in_max_a)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Pool ID", + "%lld", msg->lp_add.pool_id)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, + "Confirm Share Out Amount", "Receive %s GAMM-%lld shares?", + outsoamt, msg->lp_add.pool_id)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgLPAdd( + msg->lp_add.pool_id, msg->lp_add.sender, + msg->lp_add.share_out_amount, msg->lp_add.amount_in_max_a, + msg->lp_add.denom_in_max_a, msg->lp_add.amount_in_max_b, + msg->lp_add.denom_in_max_b)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include LP add message in transaction"); + layoutHome(); + return; + } + } else if (msg->has_lp_remove) { + /** Confirm required transaction parameters exist */ + if (!msg->lp_remove.has_sender || !msg->lp_remove.has_pool_id || + !msg->lp_remove.has_share_in_amount || + !msg->lp_remove.has_denom_out_min_a || + !msg->lp_remove.has_amount_out_min_a || + !msg->lp_remove.has_denom_out_min_b || + !msg->lp_remove.has_amount_out_min_b) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + + /** Confirm transaction parameters on-screen */ + char insoamt[33] = {0}; + uint8_t outsoamt[34] = {0}; + strlcpy(insoamt, msg->lp_remove.share_in_amount, + sizeof(msg->lp_remove.share_in_amount)); + + if (base_to_precision(outsoamt, (uint8_t *)insoamt, sizeof(outsoamt), + strlen(insoamt), OSMOSIS_LP_ASSET_PRECISION) < 0) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_Other, NULL); + layoutHome(); + return; + } + + if (!confirm( + ButtonRequestType_ButtonRequest_Other, "Remove Liquidity", + "Withdraw %.6f %s and...", + atof(msg->lp_remove.amount_out_min_b) / pow(10, OSMOSIS_PRECISION), + (!strcmp(msg->lp_remove.denom_out_min_b, "uosmo")) + ? "OSMO" + : msg->lp_remove.denom_out_min_b)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm( + ButtonRequestType_ButtonRequest_Other, "Remove Liquidity", + "... %.6f %s ?", + atof(msg->lp_remove.amount_out_min_a) / pow(10, OSMOSIS_PRECISION), + (!strcmp(msg->lp_remove.denom_out_min_a, "uosmo")) + ? "OSMO" + : msg->lp_remove.denom_out_min_a)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Pool ID", + "%lld", msg->lp_remove.pool_id)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Pool share amount", + "Redeem %s GAMM-%lld shares?", outsoamt, + msg->lp_remove.pool_id)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgLPRemove( + msg->lp_remove.pool_id, msg->lp_remove.sender, + msg->lp_remove.share_in_amount, msg->lp_remove.amount_out_min_a, + msg->lp_remove.denom_out_min_a, msg->lp_remove.amount_out_min_b, + msg->lp_remove.denom_out_min_b)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include rewards message in transaction"); + layoutHome(); + return; + } + } else if (msg->has_redelegate) { + /** Confirm required transaction parameters exist */ + if (!msg->redelegate.has_delegator_address || + !msg->redelegate.has_validator_src_address || + !msg->redelegate.has_validator_dst_address || + !msg->redelegate.has_amount) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + /** Confirm transaction parameters on-screen */ + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Redelegate", + "Redelegate %.6f OSMO?", + atof(msg->send.amount) / pow(10, OSMOSIS_PRECISION))) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Delegator Address", + msg->redelegate.delegator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Validator Source Address", + msg->redelegate.validator_src_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Validator Dest. Address", + msg->redelegate.validator_dst_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgRedelegate( + msg->redelegate.amount, msg->redelegate.delegator_address, + msg->redelegate.validator_src_address, + msg->redelegate.validator_dst_address, msg->redelegate.denom)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include redelegate message in transaction"); + layoutHome(); + return; + } + } else if (msg->has_rewards) { + /** Confirm required transaction parameters exist */ + if (!msg->rewards.has_delegator_address || + !msg->rewards.has_validator_address) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Claim Rewards", + "Claim all available rewards?")) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Confirm Delegator Address", + msg->rewards.delegator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm_osmosis_address("Confirm Validator Address", + msg->rewards.validator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgRewards(msg->rewards.delegator_address, + msg->rewards.validator_address)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include rewards message in transaction"); + layoutHome(); + return; + } + } else if (msg->has_swap) { + /** Confirm required transaction parameters exist */ + if (!msg->swap.has_sender || + !msg->swap.has_pool_id | !msg->swap.has_token_out_denom || + !msg->swap.has_token_in_denom || !msg->swap.has_token_in_amount || + !msg->swap.has_token_out_min_amount) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + + /** Confirm transaction parameters on-screen */ + + if (!confirm( + ButtonRequestType_ButtonRequest_Other, "Swap", + "Swap %.6f %s for at least %.6f %s?", + atof(msg->swap.token_in_amount) / pow(10, OSMOSIS_PRECISION), + msg->swap.token_in_denom, + atof(msg->swap.token_out_min_amount) / pow(10, OSMOSIS_PRECISION), + msg->swap.token_out_denom)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Pool ID", + "%lld", msg->swap.pool_id)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgSwap( + msg->swap.pool_id, msg->swap.token_out_denom, msg->swap.sender, + msg->swap.token_in_amount, msg->swap.token_in_denom, + msg->swap.token_out_min_amount)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include swap message in transaction"); + layoutHome(); + return; + } + + } else if (msg->has_ibc_transfer) { + /** Confirm required transaction parameters exist */ + if (!msg->ibc_transfer.has_sender || + !msg->ibc_transfer.has_source_channel || + !msg->ibc_transfer.has_source_port || + !msg->ibc_transfer.has_revision_height || + !msg->ibc_transfer.has_revision_number || + !msg->ibc_transfer.has_denom) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Message is missing required parameters")); + layoutHome(); + return; + } + /** Confirm transaction parameters on-screen */ + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "IBC Transfer", + "Transfer %.6f %s to %s?", + atof(msg->ibc_transfer.amount) / pow(10, OSMOSIS_PRECISION), + msg->ibc_transfer.denom, msg->ibc_transfer.receiver)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, + "Confirm Source Channel", "%s", + msg->ibc_transfer.source_channel)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, "Confirm Source Port", + "%s", msg->ibc_transfer.source_port)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, + "Confirm Revision Height", "%s", + msg->ibc_transfer.revision_height)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!confirm(ButtonRequestType_ButtonRequest_Other, + "Confirm Revision Number", "%s", + msg->ibc_transfer.revision_number)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (!osmosis_signTxUpdateMsgIBCTransfer( + msg->ibc_transfer.amount, msg->ibc_transfer.sender, + msg->ibc_transfer.receiver, msg->ibc_transfer.source_channel, + msg->ibc_transfer.source_port, msg->ibc_transfer.revision_number, + msg->ibc_transfer.revision_height, msg->ibc_transfer.denom)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_SyntaxError, + "Failed to include IBC transfer message in transaction"); + layoutHome(); + return; + } + } else { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_FirmwareError, + _("Invalid Osmosis message type")); + layoutHome(); + return; + } + + if (!osmosis_signingIsFinished()) { + RESP_INIT(OsmosisMsgRequest); + msg_write(MessageType_MessageType_OsmosisMsgRequest, resp); + return; + } + + if (sign_tx->has_memo && (strlen(sign_tx->memo) > 0)) { + if (!confirm(ButtonRequestType_ButtonRequest_ConfirmMemo, _("Memo"), "%s", + sign_tx->memo)) { + osmosis_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 Osmosis transaction on %s? " + "It includes a fee of %" PRIu32 " uOSMO and %" PRIu32 " gas.", + sign_tx->chain_id, sign_tx->fee_amount, sign_tx->gas)) { + osmosis_signAbort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + RESP_INIT(OsmosisSignedTx); + + if (!osmosis_signTxFinalize(resp->public_key.bytes, resp->signature.bytes)) { + osmosis_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; + osmosis_signAbort(); + layoutHome(); + msg_write(MessageType_MessageType_OsmosisSignedTx, resp); +} \ No newline at end of file diff --git a/lib/firmware/messagemap.def b/lib/firmware/messagemap.def index bc27b14d3..6097a49e0 100644 --- a/lib/firmware/messagemap.def +++ b/lib/firmware/messagemap.def @@ -50,6 +50,10 @@ MSG_IN(MessageType_MessageType_CosmosSignTx, CosmosSignTx, fsm_msgCosmosSignTx) MSG_IN(MessageType_MessageType_CosmosMsgAck, CosmosMsgAck, fsm_msgCosmosMsgAck) + MSG_IN(MessageType_MessageType_OsmosisGetAddress, OsmosisGetAddress, fsm_msgOsmosisGetAddress) + MSG_IN(MessageType_MessageType_OsmosisSignTx, OsmosisSignTx, fsm_msgOsmosisSignTx) + MSG_IN(MessageType_MessageType_OsmosisMsgAck, OsmosisMsgAck, fsm_msgOsmosisMsgAck) + MSG_IN(MessageType_MessageType_BinanceGetAddress, BinanceGetAddress, fsm_msgBinanceGetAddress) MSG_IN(MessageType_MessageType_BinanceSignTx, BinanceSignTx, fsm_msgBinanceSignTx) MSG_IN(MessageType_MessageType_BinanceTransferMsg, BinanceTransferMsg, fsm_msgBinanceTransferMsg) @@ -100,6 +104,10 @@ MSG_OUT(MessageType_MessageType_CosmosMsgRequest, CosmosMsgRequest, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_CosmosSignedTx, CosmosSignedTx, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_OsmosisAddress, OsmosisAddress, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_OsmosisMsgRequest, OsmosisMsgRequest, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_OsmosisSignedTx, OsmosisSignedTx, NO_PROCESS_FUNC) + MSG_OUT(MessageType_MessageType_BinanceAddress, BinanceAddress, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_BinancePublicKey, BinancePublicKey, NO_PROCESS_FUNC) MSG_OUT(MessageType_MessageType_BinanceTxRequest, BinanceTxRequest, NO_PROCESS_FUNC) diff --git a/lib/firmware/osmosis.c b/lib/firmware/osmosis.c new file mode 100644 index 000000000..a9ac61037 --- /dev/null +++ b/lib/firmware/osmosis.c @@ -0,0 +1,630 @@ +/* + * 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/osmosis.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 OsmosisSignTx msg; +static bool testnet; + +const OsmosisSignTx *osmosis_getOsmosisSignTx(void) { return &msg; } + +bool osmosis_signTxInit(const HDNode *_node, const OsmosisSignTx *_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] = {0}; + + 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\":\"uosmo\"}]", + 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 osmosis_signTxUpdateMsgSend(const char *amount, const char *to_address) { + char mainnetp[] = "osmo"; + char testnetp[] = "tosmo"; + char *pfix; + char buffer[64 + 1]; + + size_t decoded_len; + char hrp[45] = {0}; + uint8_t decoded[38] = {0}; + 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\":\"cosmos-sdk/MsgSend\",\"value\":{"; + sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + + // 21 + ^20 + 19 = ^60 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"amount\":[{\"amount\":\"%s\",\"denom\":\"uosmo\"}]", 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 osmosis_signTxUpdateMsgDelegate(const char *amount, + const char *delegator_address, + const char *validator_address, + const char *denom) { + char mainnetp[] = "osmo"; + char testnetp[] = "tosmo"; + char *pfix; + + char buffer[128] = {0}; + size_t decoded_len; + char hrp[45] = {0}; + uint8_t decoded[38] = {0}; + + if (!bech32_decode(hrp, decoded, &decoded_len, delegator_address)) { + return false; + } + + // ^14 + 39 + 1 = ^54 + char from_address[54] = {0}; + + pfix = mainnetp; + if (testnet) { + pfix = testnetp; + } + + if (!tendermint_getAddress(&node, pfix, from_address)) { + return false; + } + + bool success = true; + + // 9 + ^24 + 23 = ^56 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "{\"type\":\"cosmos-sdk/MsgDelegate\",\"value\":{"); + + // 20 + ^20 + 11 + ^9 + 2 = ^62 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"amount\":{\"amount\":\"%s\",\"denom\":\"%s\"}", amount, denom); + + // 22 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"delegator_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\",\"", + delegator_address); + + // 20 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "validator_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\"}}", + validator_address); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgUndelegate(const char *amount, + const char *delegator_address, + const char *validator_address, + const char *denom) { + char mainnetp[] = "osmo"; + char testnetp[] = "tosmo"; + char *pfix; + + char buffer[128] = {0}; + size_t decoded_len; + char hrp[45] = {0}; + uint8_t decoded[38] = {0}; + + if (!bech32_decode(hrp, decoded, &decoded_len, delegator_address)) { + return false; + } + + // ^14 + 39 + 1 = ^54 + char from_address[54] = {0}; + + pfix = mainnetp; + if (testnet) { + pfix = testnetp; + } + if (!tendermint_getAddress(&node, pfix, from_address)) { + return false; + } + + bool success = true; + + // 9 + ^24 + 25 = ^58 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "{\"type\":\"cosmos-sdk/MsgUndelegate\",\"value\":{"); + + // 20 + ^20 + 11 + ^9 + 2 = ^62 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"amount\":{\"amount\":\"%s\",\"denom\":\"%s\"}", amount, denom); + + // 22 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"delegator_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\",\"", + delegator_address); + + // 20 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "validator_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\"}}", + validator_address); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgRedelegate(const char *amount, + const char *delegator_address, + const char *validator_src_address, + const char *validator_dst_address, + const char *denom) { + char mainnetp[] = "osmo"; + char testnetp[] = "tosmo"; + char *pfix; + + char buffer[128] = {0}; + size_t decoded_len; + char hrp[45] = {0}; + uint8_t decoded[38] = {0}; + + if (!bech32_decode(hrp, decoded, &decoded_len, delegator_address)) { + return false; + } + + // ^14 + 39 + 1 = ^54 + char from_address[54] = {0}; + pfix = mainnetp; + if (testnet) { + pfix = testnetp; + } + if (!tendermint_getAddress(&node, pfix, from_address)) { + return false; + } + + bool success = true; + + // 9 + ^24 + 28 = ^64 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "{\"type\":\"cosmos-sdk/MsgBeginRedelegate\",\"value\""); + + // 22 + ^20 + 11 + ^9 + 2 = ^64 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + ":{\"amount\":{\"amount\":\"%s\",\"denom\":\"%s\"}", amount, denom); + + // 22 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"delegator_address\":\""); + + // ^53 + 1 = ^54 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s", + delegator_address); + + // 27 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\",\"validator_dst_address\":\""); + + // ^53 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s", + validator_dst_address); + + // 27 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\",\"validator_src_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\"}}", + validator_src_address); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgLPAdd(const uint64_t pool_id, const char *sender, + const char *share_out_amount, + const char *amount_in_max_a, + const char *denom_in_max_a, + const char *amount_in_max_b, + const char *denom_in_max_b) { + char buffer[96 + 1] = {0}; + + bool success = true; + + const char *const prelude = + "{\"type\":\"osmosis/gamm/join-pool\",\"value\":{"; + sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"pool_id\":\"%" PRIu64 "\",", pool_id); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"sender\":"); + + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"%s\",", sender); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"share_out_amount\":"); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"%s\",", + share_out_amount); + + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"token_in_maxs\":[{"); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"amount\":\"%s\",", amount_in_max_a); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"denom\":\"%s\"},", denom_in_max_a); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "{\"amount\":\"%s\",", amount_in_max_b); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"denom\":\"%s\"}]}}", denom_in_max_b); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgLPRemove(const uint64_t pool_id, const char *sender, + const char *share_out_amount, + const char *amount_out_min_a, + const char *denom_out_min_a, + const char *amount_out_min_b, + const char *denom_out_min_b) { + char buffer[96 + 1] = {0}; + + bool success = true; + + const char *const prelude = + "{\"type\":\"osmosis/gamm/exit-pool\",\"value\":{"; + sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"pool_id\":\"%" PRIu64 "\",", pool_id); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"sender\":"); + + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"%s\",", sender); + + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"share_in_amount\":"); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"%s\",", + share_out_amount); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"token_out_mins\":[{"); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"amount\":\"%s\",", amount_out_min_a); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"denom\":\"%s\"},", denom_out_min_a); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "{\"amount\":\"%s\",", amount_out_min_b); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"denom\":\"%s\"}]}}", denom_out_min_b); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgRewards(const char *delegator_address, + const char *validator_address) { + char mainnetp[] = "osmo"; + char testnetp[] = "tosmo"; + char *pfix; + + char buffer[128] = {0}; + size_t decoded_len; + char hrp[45] = {0}; + uint8_t decoded[38] = {0}; + + if (!bech32_decode(hrp, decoded, &decoded_len, delegator_address)) { + return false; + } + + pfix = mainnetp; + if (testnet) { + pfix = testnetp; + } + + // ^14 + 39 + 1 = ^54 + char from_address[54] = {0}; + if (!tendermint_getAddress(&node, pfix, from_address)) { + return false; + } + + bool success = true; + + // 9 + ^24 + 38 = ^72 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "{\"type\":\"cosmos-sdk/MsgWithdrawDelegationReward\",\"value\":{"); + + // 21 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"delegator_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\",\"", + delegator_address); + + // 20 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "validator_address\":\""); + + // ^53 + 3 = ^56 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\"}}", + validator_address); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgIBCTransfer(const char *amount, const char *sender, + const char *receiver, + const char *source_channel, + const char *source_port, + const char *revision_number, + const char *revision_height, + const char *denom) { + char mainnetp[] = "osmo"; + char testnetp[] = "tosmo"; + char *pfix; + + char buffer[128] = {0}; + size_t decoded_len; + char hrp[45] = {0}; + uint8_t decoded[38] = {0}; + + if (!bech32_decode(hrp, decoded, &decoded_len, receiver)) { + return false; + } + + pfix = mainnetp; + if (testnet) { + pfix = testnetp; + } + + // ^14 + 39 + 1 = ^54 + char from_address[54] = {0}; + if (!tendermint_getAddress(&node, pfix, from_address)) { + return false; + } + + bool success = true; + + // 9 + ^24 + 23 = ^56 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "{\"type\":\"cosmos-sdk/MsgTransfer\",\"value\":{"); + + // 13 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), "\"receiver\":\""); + + // ^53 + 1 = ^54 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\"", receiver); + + // 11 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), ",\"sender\":\""); + + // ^53 + 1 = ^54 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), "%s\"", sender); + + // 19 + ^32 + 1 = ^52 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"source_channel\":\"%s\"", source_channel); + + // 16 + ^32 + 2 = ^40 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + ",\"source_port\":\"%s\",", source_port); + + // 37 + ^16 = ^53 + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"timeout_height\":{\"revision_height\":\"%s", + revision_height); + + // 21 + ^9 + 3 = ^33 + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\",\"revision_number\":\"%s\"},", revision_number); + + // 20 + ^20 + 11 + ^9 + 3 = ^63 + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"token\":{\"amount\":\"%s\",\"denom\":\"%s\"}}}", amount, denom); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxUpdateMsgSwap(const uint64_t pool_id, + const char *token_out_denom, + const char *sender, + const char *token_in_amount, + const char *token_in_denom, + const char *token_out_min_amount) { + char buffer[96 + 1] = {0}; + + // TODO: add testnet support + + bool success = true; + + const char *const prelude = + "{\"type\":\"osmosis/gamm/swap-exact-amount-in\","; + sha256_Update(&ctx, (uint8_t *)prelude, strlen(prelude)); + + success &= tendermint_snprintf( + &ctx, buffer, sizeof(buffer), + "\"value\":{\"routes\":[{\"pool_id\":\"%" PRIu64 "\",", pool_id); + + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"token_out_denom\":\"%s\"}],", token_out_denom); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"sender\":\"%s\",", sender); + + success &= + tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"token_in\":{\"amount\":\"%s\",", token_in_amount); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"denom\":\"%s\"},", token_in_denom); + + success &= tendermint_snprintf(&ctx, buffer, sizeof(buffer), + "\"token_out_min_amount\":\"%s\"}}", + token_out_min_amount); + + msgs_remaining--; + return success; +} + +bool osmosis_signTxFinalize(uint8_t *public_key, uint8_t *signature) { + char buffer[128] = {0}; + + // 14 + ^20 + 2 = ^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 osmosis_signingIsInited(void) { return initialized; } + +bool osmosis_signingIsFinished(void) { return msgs_remaining == 0; } + +void osmosis_signAbort(void) { + initialized = false; + msgs_remaining = 0; + memzero(&msg, sizeof(msg)); + memzero(&node, sizeof(node)); +} diff --git a/lib/firmware/signtx_tendermint.c b/lib/firmware/signtx_tendermint.c index 0b28eab69..8b87ebdc9 100644 --- a/lib/firmware/signtx_tendermint.c +++ b/lib/firmware/signtx_tendermint.c @@ -398,7 +398,8 @@ bool tendermint_signTxUpdateMsgRewards(const uint64_t *amount, // 9 + ^24 + 38 = ^72 success &= tendermint_snprintf( &ctx, buffer, sizeof(buffer), - "{\"type\":\"%s/MsgWithdrawDelegationReward\",\"value\":{", msgTypePrefix); + "{\"type\":\"%s/MsgWithdrawDelegationReward\",\"value\":{", + msgTypePrefix); // 20 + ^20 + 11 + ^9 + 3 = ^65 if (amount != NULL) { @@ -537,4 +538,4 @@ void tendermint_signAbort(void) { msgs_remaining = 0; memzero(&tmsg, sizeof(tmsg)); memzero(&node, sizeof(node)); -} +} \ No newline at end of file diff --git a/lib/transport/CMakeLists.txt b/lib/transport/CMakeLists.txt index f611e03bc..22ddebd05 100644 --- a/lib/transport/CMakeLists.txt +++ b/lib/transport/CMakeLists.txt @@ -10,6 +10,7 @@ set(protoc_pb_sources ${DEVICE_PROTOCOL}/messages-nano.proto ${DEVICE_PROTOCOL}/messages-binance.proto ${DEVICE_PROTOCOL}/messages-cosmos.proto + ${DEVICE_PROTOCOL}/messages-osmosis.proto ${DEVICE_PROTOCOL}/messages-ripple.proto ${DEVICE_PROTOCOL}/messages-tendermint.proto ${DEVICE_PROTOCOL}/messages-thorchain.proto @@ -22,6 +23,7 @@ set(protoc_pb_options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-nano.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-binance.options ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-cosmos.options + ${CMAKE_SOURCE_DIR}/include/keepkey/transport/messages-osmosis.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 @@ -34,6 +36,7 @@ set(protoc_c_sources ${CMAKE_BINARY_DIR}/lib/transport/messages-nano.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages-binance.pb.c ${CMAKE_BINARY_DIR}/lib/transport/messages-cosmos.pb.c + ${CMAKE_BINARY_DIR}/lib/transport/messages-osmosis.pb.c ${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 @@ -46,6 +49,7 @@ set(protoc_c_headers ${CMAKE_BINARY_DIR}/include/messages-nano.pb.h ${CMAKE_BINARY_DIR}/include/messages-binance.pb.h ${CMAKE_BINARY_DIR}/include/messages-cosmos.pb.h + ${CMAKE_BINARY_DIR}/include/messages-osmosis.pb.h ${CMAKE_BINARY_DIR}/include/messages-ripple.pb.h ${CMAKE_BINARY_DIR}/include/messages-tendermint.pb.h ${CMAKE_BINARY_DIR}/include/messages-thorchain.pb.h @@ -58,6 +62,7 @@ set(protoc_pb_sources_moved ${CMAKE_BINARY_DIR}/lib/transport/messages-nano.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-binance.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-cosmos.proto + ${CMAKE_BINARY_DIR}/lib/transport/messages-osmosis.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-ripple.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-tendermint.proto ${CMAKE_BINARY_DIR}/lib/transport/messages-thorchain.proto @@ -106,6 +111,10 @@ add_custom_command( ${PROTOC_BINARY} -I. -I/usr/include --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb "--nanopb_out=-f messages-cosmos.options:." messages-cosmos.proto + COMMAND + ${PROTOC_BINARY} -I. -I/usr/include + --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb + "--nanopb_out=-f messages-osmosis.options:." messages-osmosis.proto COMMAND ${PROTOC_BINARY} -I. -I/usr/include --plugin=nanopb=${NANOPB_DIR}/generator/protoc-gen-nanopb