From c0210781d4a276815fad4a0182af488ac9076221 Mon Sep 17 00:00:00 2001 From: ftheirs Date: Wed, 7 Feb 2024 18:40:44 -0300 Subject: [PATCH] support for new transactions --- app/src/allowed_transactions.h | 1 + app/src/coin.h | 1 + app/src/parser_impl.c | 4 ++ app/src/parser_impl_common.c | 15 +++++ app/src/parser_impl_common.h | 4 ++ app/src/parser_impl_txn.c | 111 +++++++++++++++++++++++++------- app/src/parser_print_common.c | 2 +- app/src/parser_print_txn.c | 112 ++++++++++++++++++++++++++++++--- app/src/parser_txdef.h | 1 + app/src/parser_types.h | 22 ++++++- 10 files changed, 242 insertions(+), 31 deletions(-) diff --git a/app/src/allowed_transactions.h b/app/src/allowed_transactions.h index 5e350eae..80ffee88 100644 --- a/app/src/allowed_transactions.h +++ b/app/src/allowed_transactions.h @@ -45,6 +45,7 @@ static const txn_types_t allowed_txn[] = { {"tx_change_consensus_key.wasm", ChangeConsensusKey}, {"tx_update_steward_commission.wasm", UpdateStewardCommission}, {"tx_change_validator_metadata.wasm", ChangeValidatorMetadata}, + {"tx_bridge_pool.wasm", BridgePoolTransfer}, }; static const uint32_t allowed_txn_len = sizeof(allowed_txn) / sizeof(allowed_txn[0]); diff --git a/app/src/coin.h b/app/src/coin.h index 6bfa3520..82c12a72 100644 --- a/app/src/coin.h +++ b/app/src/coin.h @@ -35,6 +35,7 @@ extern "C" { #define COMPRESSED_SECP256K1_PK_LEN 33u #define SECP256K1_SK_LEN 32u #define SCALAR_LEN_SECP256K1 32u +#define ETH_ADDRESS_LEN 20u #define SK_LEN_25519 32u #define SCALAR_LEN_ED25519 32u diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 0f25b558..9975ed9d 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -155,6 +155,10 @@ parser_error_t getNumItems(const parser_context_t *ctx, uint8_t *numItems) { break; } + case BridgePoolTransfer: + *numItems = app_mode_expert() ? BRIDGE_POOL_TRANSFER_EXPERT_PARAMS : BRIDGE_POOL_TRANSFER_NORMAL_PARAMS; + break; + default: break; } diff --git a/app/src/parser_impl_common.c b/app/src/parser_impl_common.c index 8880e076..0d840fcb 100644 --- a/app/src/parser_impl_common.c +++ b/app/src/parser_impl_common.c @@ -81,6 +81,21 @@ parser_error_t readFieldSize(parser_context_t *ctx, uint32_t *size) { return parser_ok; } +parser_error_t readFieldSizeU16(parser_context_t *ctx, uint16_t *size) { + uint8_t consumed = 0; + uint64_t tmpSize = 0; + + decodeLEB128(ctx->buffer + ctx->offset, 10, &consumed, &tmpSize); + ctx->offset += consumed; + + if (tmpSize > UINT16_MAX) { + return parser_value_out_of_range; + } + *size = (uint16_t)tmpSize; + + return parser_ok; +} + parser_error_t checkTag(parser_context_t *ctx, uint8_t expectedTag) { uint8_t tmpTag = 0; CHECK_ERROR(readByte(ctx, &tmpTag)) diff --git a/app/src/parser_impl_common.h b/app/src/parser_impl_common.h index 3c2925b5..46773132 100644 --- a/app/src/parser_impl_common.h +++ b/app/src/parser_impl_common.h @@ -81,6 +81,9 @@ extern "C" { #define CHANGE_VALIDATOR_METADATA_NORMAL_PARAMS 2 #define CHANGE_VALIDATOR_METADATA_EXPERT_PARAMS 7 +#define BRIDGE_POOL_TRANSFER_NORMAL_PARAMS 9 +#define BRIDGE_POOL_TRANSFER_EXPERT_PARAMS 14 + parser_error_t readByte(parser_context_t *ctx, uint8_t *byte); parser_error_t readBytes(parser_context_t *ctx, const uint8_t **output, uint16_t outputLen); parser_error_t readUint16(parser_context_t *ctx, uint16_t *value); @@ -88,6 +91,7 @@ parser_error_t readUint32(parser_context_t *ctx, uint32_t *value); parser_error_t readUint64(parser_context_t *ctx, uint64_t *value); parser_error_t readFieldSize(parser_context_t *ctx, uint32_t *size); +parser_error_t readFieldSizeU16(parser_context_t *ctx, uint16_t *size); parser_error_t checkTag(parser_context_t *ctx, uint8_t expectedTag); parser_error_t readPubkey(parser_context_t *ctx, bytes_t *pubkey); diff --git a/app/src/parser_impl_txn.c b/app/src/parser_impl_txn.c index aac7d407..333c96d8 100644 --- a/app/src/parser_impl_txn.c +++ b/app/src/parser_impl_txn.c @@ -727,6 +727,41 @@ static parser_error_t readChangeValidatorMetadata(bytes_t *buffer, tx_metadata_c return parser_ok; } +static parser_error_t readBridgePoolTransfer(const bytes_t *data, tx_bridge_pool_transfer_t *bridgePoolTransfer) { + parser_context_t ctx = {.buffer = data->ptr, .bufferLen = data->len, .offset = 0, .tx_obj = NULL}; + + CHECK_ERROR(readByte(&ctx, &bridgePoolTransfer->kind)) + if (bridgePoolTransfer->kind > Nut) { + return parser_value_out_of_range; + } + + bridgePoolTransfer->asset.len = ETH_ADDRESS_LEN; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->asset.ptr, bridgePoolTransfer->asset.len)) + + bridgePoolTransfer->recipient.len = ETH_ADDRESS_LEN; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->recipient.ptr, bridgePoolTransfer->recipient.len)) + + bridgePoolTransfer->sender.len = ADDRESS_LEN_BYTES; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->sender.ptr, bridgePoolTransfer->sender.len)) + + bridgePoolTransfer->amount.len = 32; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->amount.ptr, bridgePoolTransfer->amount.len)) + + bridgePoolTransfer->gasAmount.len = 32; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->gasAmount.ptr, bridgePoolTransfer->gasAmount.len)) + + bridgePoolTransfer->gasPayer.len = ADDRESS_LEN_BYTES; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->gasPayer.ptr, bridgePoolTransfer->gasPayer.len)) + + bridgePoolTransfer->gasToken.len = ADDRESS_LEN_BYTES; + CHECK_ERROR(readBytes(&ctx, &bridgePoolTransfer->gasToken.ptr, bridgePoolTransfer->gasToken.len)) + + if (ctx.offset != ctx.bufferLen) { + return parser_unexpected_characters; + } + return parser_ok; +} + __Z_INLINE parser_error_t readTimestamp(parser_context_t *ctx, timestamp_t *timestamp) { uint8_t consumed = 0; uint64_t tmp = 0; @@ -749,60 +784,88 @@ static parser_error_t readIBCTxn(const bytes_t *data, parser_tx_t *v) { // Read tag CHECK_ERROR(checkTag(&ctx, 0x0A)) // Skip URL: /ibc.applications.transfer.v1.MsgTransfer - uint8_t tmp = 0; - CHECK_ERROR(readByte(&ctx, &tmp)) - ctx.offset += tmp; + uint16_t tmpFieldLen = 0; + CHECK_ERROR(readFieldSizeU16(&ctx, &tmpFieldLen)) + bytes_t tmpUrl = {.ptr = NULL, .len = (uint16_t)tmpFieldLen}; + CHECK_ERROR(readBytes(&ctx, &tmpUrl.ptr, tmpUrl.len)) + // Check value field (expect vector and check size) CHECK_ERROR(checkTag(&ctx, 0x12)) - // Missing bytes - CHECK_ERROR(readByte(&ctx, &tmp)) - CHECK_ERROR(readByte(&ctx, &tmp)) + CHECK_ERROR(readFieldSizeU16(&ctx, &tmpFieldLen)) + + if (tmpFieldLen != ctx.bufferLen - ctx.offset) { + return parser_unexpected_buffer_end; + } // Read port id CHECK_ERROR(checkTag(&ctx, 0x0A)) - CHECK_ERROR(readByte(&ctx, &tmp)) - v->ibc.port_id.len = tmp; + CHECK_ERROR(readFieldSizeU16(&ctx, &v->ibc.port_id.len)) CHECK_ERROR(readBytes(&ctx, &v->ibc.port_id.ptr, v->ibc.port_id.len)) // Read channel id CHECK_ERROR(checkTag(&ctx, 0x12)) - CHECK_ERROR(readByte(&ctx, &tmp)) - v->ibc.channel_id.len = tmp; + CHECK_ERROR(readFieldSizeU16(&ctx, &v->ibc.channel_id.len)) CHECK_ERROR(readBytes(&ctx, &v->ibc.channel_id.ptr, v->ibc.channel_id.len)) + ////// Packed data // Read token address CHECK_ERROR(checkTag(&ctx, 0x1A)) - CHECK_ERROR(readByte(&ctx, &tmp)) + CHECK_ERROR(readFieldSizeU16(&ctx, &tmpFieldLen)) + CHECK_ERROR(checkTag(&ctx, 0x0A)) - CHECK_ERROR(readByte(&ctx, &tmp)) - v->ibc.token_address.len = tmp; + CHECK_ERROR(readFieldSizeU16(&ctx, &v->ibc.token_address.len)) CHECK_ERROR(readBytes(&ctx, &v->ibc.token_address.ptr, v->ibc.token_address.len)) // Read token amount CHECK_ERROR(checkTag(&ctx, 0x12)) - CHECK_ERROR(readByte(&ctx, &tmp)) - v->ibc.token_amount.len = tmp; + CHECK_ERROR(readFieldSizeU16(&ctx, &v->ibc.token_amount.len)) CHECK_ERROR(readBytes(&ctx, &v->ibc.token_amount.ptr, v->ibc.token_amount.len)) // Read sender - CHECK_ERROR(checkTag(&ctx, 0x22)) - CHECK_ERROR(readByte(&ctx, &tmp)) - v->ibc.sender_address.len = tmp; - CHECK_ERROR(readBytes(&ctx, &v->ibc.sender_address.ptr, v->ibc.sender_address.len)) + if (*(ctx.buffer + ctx.offset) == 0x22) { + CHECK_ERROR(checkTag(&ctx, 0x22)) + CHECK_ERROR(readFieldSizeU16(&ctx, &v->ibc.sender_address.len)) + CHECK_ERROR(readBytes(&ctx, &v->ibc.sender_address.ptr, v->ibc.sender_address.len)) + } // Read receiver CHECK_ERROR(checkTag(&ctx, 0x2A)) - CHECK_ERROR(readByte(&ctx, &tmp)) - v->ibc.receiver.len = tmp; + CHECK_ERROR(readFieldSizeU16(&ctx, &v->ibc.receiver.len)) CHECK_ERROR(readBytes(&ctx, &v->ibc.receiver.ptr, v->ibc.receiver.len)) + //////////////// // Read timeout height CHECK_ERROR(checkTag(&ctx, 0x32)) - CHECK_ERROR(readByte(&ctx, &v->ibc.timeout_height)) + CHECK_ERROR(readByte(&ctx, &v->ibc.timeout_height_type)) + + if (v->ibc.timeout_height_type > 0) { + uint8_t consumed = 0; + uint64_t tmp = 0; + + // Read 0x08 + CHECK_ERROR(checkTag(&ctx, 0x08)) + const uint64_t remainingBytes = ctx.bufferLen - ctx.offset; + decodeLEB128(ctx.buffer + ctx.offset, remainingBytes, &consumed, &tmp); + v->ibc.revision_number = tmp; + ctx.offset += consumed; + CHECK_ERROR(checkTag(&ctx, 0x10)) + const uint64_t remainingBytes2 = ctx.bufferLen - ctx.offset; + decodeLEB128(ctx.buffer + ctx.offset, remainingBytes2, &consumed, &tmp); + v->ibc.revision_height = tmp; + ctx.offset += consumed; + } // Read timeout timestamp CHECK_ERROR(readTimestamp(&ctx, &v->ibc.timeout_timestamp)) + CHECK_ERROR(checkTag(&ctx, 0x42)) + bytes_t tmpBytes = {0}; + CHECK_ERROR(readFieldSizeU16(&ctx, &tmpBytes.len)) + CHECK_ERROR(readBytes(&ctx, &tmpBytes.ptr, tmpBytes.len)) + + if (ctx.offset != ctx.bufferLen) { + return parser_unexpected_characters; + } return parser_ok; } @@ -1245,6 +1308,10 @@ parser_error_t validateTransactionParams(parser_tx_t *txObj) { CHECK_ERROR(readChangeValidatorMetadata(&data->bytes, &txObj->metadataChange)) break; + case BridgePoolTransfer: + CHECK_ERROR(readBridgePoolTransfer(&data->bytes, &txObj->bridgePoolTransfer)) + break; + default: return parser_unexpected_method; } diff --git a/app/src/parser_print_common.c b/app/src/parser_print_common.c index 7c973291..68740f67 100644 --- a/app/src/parser_print_common.c +++ b/app/src/parser_print_common.c @@ -141,7 +141,7 @@ parser_error_t printAmount( const bytes_t *amount, bool isSigned, uint8_t amount if (insertDecimalPoint(strAmount + isNegative, sizeof(strAmount) - isNegative, amountDenom) != zxerr_ok) { return parser_unexpected_error; } - +// const char *suffix = (amountDenom == 0) ? ".0" : ""; z_str3join(strAmount, sizeof(strAmount), symbol, ""); number_inplace_trimming(strAmount, 1); pageString(outVal, outValLen, strAmount, pageIdx, pageCount); diff --git a/app/src/parser_print_txn.c b/app/src/parser_print_txn.c index 0b1b4b65..db4767e6 100644 --- a/app/src/parser_print_txn.c +++ b/app/src/parser_print_txn.c @@ -593,6 +593,10 @@ static parser_error_t printUpdateVPTxn(const parser_context_t *ctx, if (adjustedDisplayIdx >= 3 && !updateVp->has_threshold) { adjustedDisplayIdx++; } + // Bump adjustedDisplayIdx if vp_code is not present + if (adjustedDisplayIdx >= 4 && !updateVp->has_vp_code) { + adjustedDisplayIdx++; + } switch (adjustedDisplayIdx) { case 0: @@ -644,7 +648,7 @@ static parser_error_t printUpdateVPTxn(const parser_context_t *ctx, if (!app_mode_expert()) { return parser_display_idx_out_of_range; } - displayIdx -= 4 + pubkeys_num - (updateVp->has_threshold ? 0 : 1); + displayIdx -= 4 + pubkeys_num - (updateVp->has_threshold ? 0 : 1) - (updateVp->has_vp_code ? 0 : 1); return printExpert(ctx, displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount); } @@ -841,7 +845,7 @@ static parser_error_t printIBCTxn( const parser_context_t *ctx, uint8_t pageIdx, uint8_t *pageCount) { const tx_ibc_t *ibc = &ctx->tx_obj->ibc; - char buffer[100] = {0}; + char buffer[2000] = {0}; switch (displayIdx) { case 0: @@ -882,10 +886,20 @@ static parser_error_t printIBCTxn( const parser_context_t *ctx, case 6: snprintf(outKey, outKeyLen, "Timeout height"); - if (ibc->timeout_height != 0) { - return parser_unexpected_value; + if (ibc->timeout_height_type == 0x00) { + snprintf(outVal, outValLen, "no timeout"); + } else { + char tmpBuffer[45] = {0}; + if (uint64_to_str(tmpBuffer, sizeof(tmpBuffer), ibc->revision_number) != NULL) { + return parser_unexpected_error; + } + const uint8_t tmpOffset = strnlen(tmpBuffer, sizeof(tmpBuffer)); + tmpBuffer[tmpOffset] = '-'; + if (uint64_to_str(tmpBuffer + tmpOffset + 1, sizeof(tmpBuffer) - tmpOffset - 1, ibc->revision_height) != NULL) { + return parser_unexpected_error; + } + pageString(outVal, outValLen, tmpBuffer, pageIdx, pageCount); } - snprintf(outVal, outValLen, "no timeout"); break; case 7: { @@ -894,8 +908,11 @@ static parser_error_t printIBCTxn( const parser_context_t *ctx, if (extractTime(ibc->timeout_timestamp.millis, &date) != zxerr_ok) { return parser_unexpected_error; } - snprintf(outVal, outValLen, "%04d-%02d-%02dT%02d:%02d:%02d.%09dZ", - date.tm_year, date.tm_mon, date.tm_day, date.tm_hour, date.tm_min, date.tm_sec, ibc->timeout_timestamp.nanos); + char sec_nanos[15] = {0}; + snprintf(sec_nanos, sizeof(sec_nanos), "%02d.%09d", date.tm_sec, ibc->timeout_timestamp.nanos); + number_inplace_trimming(sec_nanos, 0); + snprintf(outVal, outValLen, "%04d-%02d-%02dT%02d:%02d:%sZ", + date.tm_year, date.tm_mon, date.tm_day, date.tm_hour, date.tm_min, sec_nanos); break; } @@ -1050,6 +1067,85 @@ static parser_error_t printChangeValidatorMetadata( const parser_context_t *ctx return parser_ok; } +static parser_error_t printBridgePoolTransfer( const parser_context_t *ctx, + uint8_t displayIdx, + char *outKey, uint16_t outKeyLen, + char *outVal, uint16_t outValLen, + uint8_t pageIdx, uint8_t *pageCount) { + + tx_bridge_pool_transfer_t *bridgePoolTransfer = &ctx->tx_obj->bridgePoolTransfer; + char tmpBuffer[45] = {0}; + switch (displayIdx) { + case 0: + snprintf(outKey, outKeyLen, "Type"); + snprintf(outVal, outValLen, "Bridge Pool Transfer"); + if (app_mode_expert()) { + CHECK_ERROR(printCodeHash(&ctx->tx_obj->transaction.sections.code.bytes, outKey, outKeyLen, + outVal, outValLen, pageIdx, pageCount)) + } + break; + case 1: { + snprintf(outKey, outKeyLen, "Transfer Kind"); + if (bridgePoolTransfer->kind == Nut) { + snprintf(outVal, outValLen, "NUT"); + } else { + snprintf(outVal, outValLen, "ERC20"); + } + break; + } + case 2: { + snprintf(outKey, outKeyLen, "Transfer Sender"); + CHECK_ERROR(printAddress(bridgePoolTransfer->sender, outVal, outValLen, pageIdx, pageCount)) + break; + } + case 3: { + snprintf(outKey, outKeyLen, "Transfer Recipient"); + tmpBuffer[0] = '0'; + tmpBuffer[1] = 'x'; + array_to_hexstr(tmpBuffer + 2, sizeof(tmpBuffer) - 2, bridgePoolTransfer->recipient.ptr, bridgePoolTransfer->recipient.len); + pageStringExt(outVal, outValLen, tmpBuffer, sizeof(tmpBuffer), pageIdx, pageCount); + break; + } + case 4: { + snprintf(outKey, outKeyLen, "Transfer Asset"); + tmpBuffer[0] = '0'; + tmpBuffer[1] = 'x'; + array_to_hexstr(tmpBuffer + 2, sizeof(tmpBuffer) - 2, bridgePoolTransfer->asset.ptr, bridgePoolTransfer->asset.len); + pageStringExt(outVal, outValLen, tmpBuffer, sizeof(tmpBuffer), pageIdx, pageCount); + break; + } + case 5: { + snprintf(outKey, outKeyLen, "Transfer Amount"); + CHECK_ERROR(printAmount(&bridgePoolTransfer->amount, false, 0, "", outVal, outValLen, pageIdx, pageCount)) + break; + } + case 6: { + snprintf(outKey, outKeyLen, "Gas Payer"); + CHECK_ERROR(printAddress(bridgePoolTransfer->gasPayer, outVal, outValLen, pageIdx, pageCount)) + break; + } + case 7: { + snprintf(outKey, outKeyLen, "Gas Token"); + CHECK_ERROR(printAddress(bridgePoolTransfer->gasToken, outVal, outValLen, pageIdx, pageCount)) + break; + } + case 8: { + snprintf(outKey, outKeyLen, "Gas Amount"); + CHECK_ERROR(printAmount(&bridgePoolTransfer->gasAmount, false, 0, "", outVal, outValLen, pageIdx, pageCount)) + break; + } + default: { + if (!app_mode_expert()) { + return parser_display_idx_out_of_range; + } + displayIdx -= 9; + return printExpert(ctx, displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount); + } + } + + return parser_ok; +} + parser_error_t printTxnFields(const parser_context_t *ctx, uint8_t displayIdx, char *outKey, uint16_t outKeyLen, @@ -1117,6 +1213,8 @@ parser_error_t printTxnFields(const parser_context_t *ctx, case ChangeValidatorMetadata: return printChangeValidatorMetadata(ctx, displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount); + case BridgePoolTransfer: + return printBridgePoolTransfer(ctx, displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount); default: break; } diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index 50c4237b..8e53a996 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -194,6 +194,7 @@ typedef struct{ tx_consensus_key_change_t consensusKeyChange; tx_update_steward_commission_t updateStewardCommission; tx_metadata_change_t metadataChange; + tx_bridge_pool_transfer_t bridgePoolTransfer; }; transaction_t transaction; diff --git a/app/src/parser_types.h b/app/src/parser_types.h index 16512ba0..b0484616 100644 --- a/app/src/parser_types.h +++ b/app/src/parser_types.h @@ -52,6 +52,7 @@ typedef enum { ChangeConsensusKey, UpdateStewardCommission, ChangeValidatorMetadata, + BridgePoolTransfer, } transaction_type_e; typedef enum { @@ -81,6 +82,11 @@ typedef enum { PGFTargetIBC = 1, } pgf_target_type_e; +typedef enum { + Erc20 = 0, + Nut = 1, +} transfer_to_ethereum_kind_e; + // Structure to match the Rust serialized Decimal format typedef struct { int64_t num; @@ -249,7 +255,9 @@ typedef struct { bytes_t token_amount; bytes_t sender_address; bytes_t receiver; - uint8_t timeout_height; + uint8_t timeout_height_type; + uint64_t revision_number; + uint64_t revision_height; timestamp_t timeout_timestamp; } tx_ibc_t; @@ -268,6 +276,18 @@ typedef struct { bytes_t commission; } tx_update_steward_commission_t; +typedef struct { + uint8_t kind; + bytes_t asset; + bytes_t recipient; + bytes_t sender; + bytes_t amount; + + bytes_t gasToken; + bytes_t gasAmount; + bytes_t gasPayer; +} tx_bridge_pool_transfer_t; + typedef struct { bytes_t validator; bytes_t email;