Skip to content

Commit

Permalink
Merge pull request #8 from Zondax/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
chcmedeiros authored Mar 11, 2024
2 parents ffc9ce3 + c220e2c commit 095303c
Show file tree
Hide file tree
Showing 145 changed files with 8,910 additions and 228 deletions.
2 changes: 1 addition & 1 deletion app/Makefile.version
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ APPVERSION_M=0
# This is the minor version
APPVERSION_N=0
# This is the patch version
APPVERSION_P=9
APPVERSION_P=10
2 changes: 2 additions & 0 deletions app/src/crypto_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ extern "C" {
} while (0)

#define MAX_BECH32_HRP_LEN 83u
#define SELECTOR_LENGTH 4
#define BIGINT_LENGTH 32

extern uint8_t bech32_hrp_len;
extern char bech32_hrp[MAX_BECH32_HRP_LEN + 1];
Expand Down
45 changes: 22 additions & 23 deletions app/src/eth_erc20.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,59 +20,56 @@

// Prefix is calculated as: keccak256("transfer(address,uint256)") = 0xa9059cbb
const uint8_t ERC20_TRANSFER_PREFIX[] = {0xa9, 0x05, 0x9c, 0xbb};
#define ERC20_DATA_LENGTH 68 // 4 + 32 + 32
#define ADDRESS_CONTRACT_LENGTH 20

#define DECIMAL_BASE 10
const erc20_tokens_t supportedTokens[] = {
{{0x1D, 0x80, 0xc4, 0x9B, 0xbB, 0xCd, 0x1C, 0x09, 0x11, 0x34,
0x66, 0x56, 0xB5, 0x29, 0xDF, 0x9E, 0x5c, 0x2F, 0x78, 0x3d},
"WFLR ",
18},
{{0x02, 0xf0, 0x82, 0x6e, 0xf6, 0xad, 0x10, 0x7c, 0xfc, 0x86,
0x11, 0x52, 0xb3, 0x2b, 0x52, 0xfd, 0x11, 0xba, 0xb9, 0xed},
"WSGB ",
18},

};

parser_error_t getERC20Token(const rlp_t *data, char tokenSymbol[MAX_SYMBOL_LEN], uint8_t *decimals) {
if (data == NULL || tokenSymbol == NULL || decimals == NULL || data->rlpLen != ERC20_DATA_LENGTH ||
memcmp(data->ptr, ERC20_TRANSFER_PREFIX, 4) != 0) {
parser_error_t getERC20Token(const eth_tx_t *ethObj, char tokenSymbol[MAX_SYMBOL_LEN], uint8_t *decimals) {
if (ethObj == NULL || tokenSymbol == NULL || decimals == NULL || ethObj->legacy.data.rlpLen != ERC20_DATA_LENGTH ||
memcmp(ethObj->legacy.data.ptr, ERC20_TRANSFER_PREFIX, 4) != 0) {
return parser_unexpected_value;
}

// Verify address contract: first 12 bytes must be 0
const uint8_t *addressPtr = data->ptr + 4;
for (uint8_t i = 0; i < 12; i++) {
if (*(addressPtr++) != 0) {
return parser_unexpected_value;
}
}

// Check if token is in the list
const uint8_t supportedTokensSize = sizeof(supportedTokens) / sizeof(supportedTokens[0]);
for (uint8_t i = 0; i < supportedTokensSize; i++) {
if (memcmp(addressPtr, supportedTokens[i].address, ADDRESS_CONTRACT_LENGTH) == 0) {
if (memcmp(ethObj->legacy.to.ptr, supportedTokens[i].address, ETH_ADDRESS_LEN) == 0) {
// Set symbol and decimals
snprintf(tokenSymbol, 10, "%s", (char *)PIC(supportedTokens[i].symbol));
*decimals = supportedTokens[i].decimals;
return parser_ok;
}
}

// Unknonw token
snprintf(tokenSymbol, 10, "?? ");
// WNAT token
snprintf(tokenSymbol, 10, "WNAT ");
*decimals = 0;
return parser_ok;
}
parser_error_t printERC20Value(const rlp_t *data, char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) {
if (data == NULL || outVal == NULL || pageCount == NULL) {
parser_error_t printERC20Value(const eth_tx_t *ethObj, char *outVal, uint16_t outValLen, uint8_t pageIdx,
uint8_t *pageCount) {
if (ethObj == NULL || outVal == NULL || pageCount == NULL) {
return parser_unexpected_error;
}

// [identifier (4) | token contract (12 + 20) | value (32)]
char tokenSymbol[10] = {0};
uint8_t decimals = 0;
CHECK_ERROR(getERC20Token(data, tokenSymbol, &decimals))
CHECK_ERROR(getERC20Token(ethObj, tokenSymbol, &decimals))

uint256_t value = {0};
const uint8_t *valuePtr = data->ptr + 4 + 12 + ADDRESS_CONTRACT_LENGTH;
parser_context_t tmpCtx = {.buffer = valuePtr, .bufferLen = 32, .offset = 0, .tx_type = eth_tx};
const uint8_t *valuePtr = ethObj->legacy.data.ptr + SELECTOR_LENGTH + BIGINT_LENGTH;
parser_context_t tmpCtx = {.buffer = valuePtr, .bufferLen = BIGINT_LENGTH, .offset = 0, .tx_type = eth_tx};
CHECK_ERROR(readu256BE(&tmpCtx, &value));

char bufferUI[100] = {0};
Expand All @@ -95,9 +92,11 @@ parser_error_t printERC20Value(const rlp_t *data, char *outVal, uint16_t outValL
return parser_ok;
}

bool validateERC20(rlp_t data) {
bool validateERC20(eth_tx_t *ethObj) {
// Check that data start with ERC20 prefix
if (data.ptr == NULL || data.rlpLen != ERC20_DATA_LENGTH || memcmp(data.ptr, ERC20_TRANSFER_PREFIX, 4) != 0) {
if (ethObj == NULL || ethObj->legacy.to.rlpLen != ETH_ADDRESS_LEN || ethObj->legacy.data.ptr == NULL ||
ethObj->legacy.data.rlpLen != ERC20_DATA_LENGTH ||
memcmp(ethObj->legacy.data.ptr, ERC20_TRANSFER_PREFIX, sizeof(ERC20_TRANSFER_PREFIX)) != 0) {
return false;
}

Expand Down
11 changes: 8 additions & 3 deletions app/src/eth_erc20.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,28 @@
#include <stdint.h>

#include "coin.h"
#include "crypto_helper.h"
#include "parser_common.h"
#include "parser_impl_eth.h"
#include "rlp.h"

#ifdef __cplusplus
extern "C" {
#endif

#define ERC20_DATA_LENGTH 68 // 4 + 32 + 32
#define ADDRESS_CONTRACT_LENGTH 20
#define MAX_SYMBOL_LEN 10
typedef struct {
uint8_t address[ETH_ADDR_LEN];
char symbol[MAX_SYMBOL_LEN];
uint8_t decimals;
} erc20_tokens_t;

bool validateERC20(rlp_t data);
parser_error_t getERC20Token(const rlp_t *data, char tokenSymbol[MAX_SYMBOL_LEN], uint8_t *decimals);
parser_error_t printERC20Value(const rlp_t *data, char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount);
bool validateERC20(eth_tx_t *ethObj);
parser_error_t getERC20Token(const eth_tx_t *ethObj, char tokenSymbol[MAX_SYMBOL_LEN], uint8_t *decimals);
parser_error_t printERC20Value(const eth_tx_t *ethObj, char *outVal, uint16_t outValLen, uint8_t pageIdx,
uint8_t *pageCount);

#ifdef __cplusplus
}
Expand Down
93 changes: 54 additions & 39 deletions app/src/parser_impl_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ eth_tx_t eth_tx_obj;
#define FLARE_MAINNET_CHAINID 14
#define SONG_BIRD_CHAINID 19
#define COSTON2_CHAINID 114
#define DATA_BYTES_TO_PRINT 10

static parser_error_t readChainID(parser_context_t *ctx, rlp_t *chainId) {
if (ctx == NULL || chainId == NULL) {
Expand Down Expand Up @@ -174,59 +175,89 @@ parser_error_t _readEth(parser_context_t *ctx, eth_tx_t *tx_obj) {
}

parser_error_t _validateTxEth() {
if (!validateERC20(eth_tx_obj.legacy.data) && !app_mode_expert()) {
if (!validateERC20(&eth_tx_obj) && !app_mode_expert()) {
return parser_unsupported_tx;
}

return parser_ok;
}

static parser_error_t printERC20(uint8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen,
uint8_t pageIdx, uint8_t *pageCount) {
static parser_error_t printEthHash(const parser_context_t *ctx, char *outKey, uint16_t outKeyLen, char *outVal,
uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) {
// we need to get keccak hash of the transaction data
uint8_t hash[32] = {0};
#if defined(TARGET_NANOS) || defined(TARGET_NANOS2) || defined(TARGET_NANOX) || defined(TARGET_STAX)
keccak_digest(ctx->buffer, ctx->bufferLen, hash, 32);
#endif

// now get the hex string of the hash
char hex[65] = {0};
array_to_hexstr(hex, 65, hash, 32);

snprintf(outKey, outKeyLen, "Eth-Hash");

pageString(outVal, outValLen, hex, pageIdx, pageCount);

return parser_ok;
}

static parser_error_t printERC20(const parser_context_t *ctx, uint8_t displayIdx, char *outKey, uint16_t outKeyLen,
char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) {
if (outKey == NULL || outVal == NULL || pageCount == NULL) {
return parser_unexpected_error;
}
MEMZERO(outKey, outKeyLen);
MEMZERO(outVal, outValLen);
*pageCount = 1;

const eth_base_t *legacy = &eth_tx_obj.legacy;
char tokenSymbol[10] = {0};
uint8_t decimals = 0;
CHECK_ERROR(getERC20Token(&eth_tx_obj.legacy.data, tokenSymbol, &decimals));
bool hideContract = (memcmp(tokenSymbol, "?? ", 3) != 0);

displayIdx += (displayIdx && hideContract) ? 1 : 0;
char data_array[40] = {0};
switch (displayIdx) {
case 0:
snprintf(outKey, outKeyLen, "To");
CHECK_ERROR(printEVMAddress(&legacy->to, outVal, outValLen, pageIdx, pageCount));
snprintf(outKey, outKeyLen, "Receiver");
rlp_t to = {.kind = RLP_KIND_STRING, .ptr = (eth_tx_obj.legacy.data.ptr + 4 + 12), .rlpLen = ETH_ADDRESS_LEN};
CHECK_ERROR(printEVMAddress(&to, outVal, outValLen, pageIdx, pageCount));
break;

case 1:
snprintf(outKey, outKeyLen, "Contract");
rlp_t contractAddress = {.kind = RLP_KIND_STRING, .ptr = (legacy->data.ptr + 4 + 12), .rlpLen = 20};
rlp_t contractAddress = {.kind = RLP_KIND_STRING, .ptr = eth_tx_obj.legacy.to.ptr, .rlpLen = ETH_ADDRESS_LEN};
CHECK_ERROR(printEVMAddress(&contractAddress, outVal, outValLen, pageIdx, pageCount));
break;

case 2:
snprintf(outKey, outKeyLen, "Value");
CHECK_ERROR(printERC20Value(&legacy->data, outVal, outValLen, pageIdx, pageCount));
snprintf(outKey, outKeyLen, "Amount");
CHECK_ERROR(printERC20Value(&eth_tx_obj, outVal, outValLen, pageIdx, pageCount));
break;

case 3:
snprintf(outKey, outKeyLen, "Nonce");
CHECK_ERROR(printRLPNumber(&legacy->nonce, outVal, outValLen, pageIdx, pageCount));
CHECK_ERROR(printRLPNumber(&eth_tx_obj.legacy.nonce, outVal, outValLen, pageIdx, pageCount));
break;

case 4:
snprintf(outKey, outKeyLen, "Gas price");
CHECK_ERROR(printRLPNumber(&legacy->gasPrice, outVal, outValLen, pageIdx, pageCount));
CHECK_ERROR(printRLPNumber(&eth_tx_obj.legacy.gasPrice, outVal, outValLen, pageIdx, pageCount));
break;

case 5:
snprintf(outKey, outKeyLen, "Gas limit");
CHECK_ERROR(printRLPNumber(&legacy->gasLimit, outVal, outValLen, pageIdx, pageCount));
CHECK_ERROR(printRLPNumber(&eth_tx_obj.legacy.gasLimit, outVal, outValLen, pageIdx, pageCount));
break;

case 6:
snprintf(outKey, outKeyLen, "Value");
CHECK_ERROR(printRLPNumber(&eth_tx_obj.legacy.value, outVal, outValLen, pageIdx, pageCount));
break;

case 7:
snprintf(outKey, outKeyLen, "Data");
array_to_hexstr(data_array, sizeof(data_array), eth_tx_obj.legacy.data.ptr, DATA_BYTES_TO_PRINT);
snprintf(data_array + (2 * DATA_BYTES_TO_PRINT), 4, "...");
pageString(outVal, outValLen, data_array, pageIdx, pageCount);
break;

case 8:
CHECK_ERROR(printEthHash(ctx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount));
break;

default:
Expand All @@ -239,8 +270,8 @@ static parser_error_t printERC20(uint8_t displayIdx, char *outKey, uint16_t outK
parser_error_t _getItemEth(const parser_context_t *ctx, uint8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal,
uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) {
// At the moment, clear signing is available only for ERC20
if (validateERC20(eth_tx_obj.legacy.data)) {
return printERC20(displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount);
if (validateERC20(&eth_tx_obj)) {
return printERC20(ctx, displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount);
}

// Otherwise, check that ExpertMode is enabled
Expand All @@ -257,17 +288,7 @@ parser_error_t _getItemEth(const parser_context_t *ctx, uint8_t displayIdx, char
}

// we need to get keccak hash of the transaction data
uint8_t hash[32] = {0};
keccak_digest(ctx->buffer, ctx->bufferLen, hash, 32);

// now get the hex string of the hash
char hex[65] = {0};
array_to_hexstr(hex, 65, hash, 32);

snprintf(outKey, outKeyLen, "Eth-Hash:");

pageString(outVal, outValLen, hex, pageIdx, pageCount);

CHECK_ERROR(printEthHash(ctx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount));
return parser_ok;
}

Expand All @@ -279,17 +300,11 @@ parser_error_t _getNumItemsEth(uint8_t *numItems) {
return parser_unexpected_error;
}
// Verify that tx is ERC20

if (validateERC20(eth_tx_obj.legacy.data)) {
char tokenSymbol[10] = {0};
uint8_t decimals = 0;
CHECK_ERROR(getERC20Token(&eth_tx_obj.legacy.data, tokenSymbol, &decimals));
// If token is not recognized, print value address
*numItems = (memcmp(tokenSymbol, "?? ", 3) != 0) ? 5 : 6;
if (validateERC20(&eth_tx_obj)) {
*numItems = 9;
return parser_ok;
}

// Warning message and the eth transaction hash for now.
*numItems = 2;
return parser_ok;
}
Expand Down
29 changes: 20 additions & 9 deletions tests/expected_output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,27 @@ std::vector<std::string> EVMGenerateExpectedUIOutput(const Json::Value &json, bo

///
auto message = json["message"];
auto to = message["To"].asString();
auto receiver = message["Receiver"].asString();
auto contract = message["Contract"].asString();
auto value = message["Value"].asString();
auto amount = message["Amount"].asString();
auto nonce = message["Nonce"].asString();
auto gasPrice = message["GasPrice"].asString();
auto gasLimit = message["GasLimit"].asString();
auto value = message["Value"].asString();
auto txhash = message["Eth-Hash"].asString();
auto data = message["Data"].asString();
///

uint8_t idx = 0;
auto destAddress = FormatEthAddress("To", idx, to);
auto destAddress = FormatEthAddress("Receiver", idx, receiver);
answer.insert(answer.end(), destAddress.begin(), destAddress.end());

if (value.compare(0, 2, "??") == 0) {
idx++;
auto contractAddress = FormatEthAddress("Contract", idx, contract);
answer.insert(answer.end(), contractAddress.begin(), contractAddress.end());
}
idx++;
auto contractAddress = FormatEthAddress("Contract", idx, contract);
answer.insert(answer.end(), contractAddress.begin(), contractAddress.end());

idx++;
addTo(answer, "{} | Value : {}", idx, value);
addTo(answer, "{} | Amount : {}", idx, amount);

idx++;
addTo(answer, "{} | Nonce : {}", idx, nonce);
Expand All @@ -94,5 +95,15 @@ std::vector<std::string> EVMGenerateExpectedUIOutput(const Json::Value &json, bo
idx++;
addTo(answer, "{} | Gas limit : {}", idx, gasLimit);

idx++;
addTo(answer, "{} | Value : {}", idx, value);

idx++;
addTo(answer, "{} | Data : {}", idx, data);

idx++;
auto hash = FormatEthAddress("Eth-Hash", idx, txhash);
answer.insert(answer.end(), hash.begin(), hash.end());

return answer;
}
Loading

0 comments on commit 095303c

Please sign in to comment.