diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d088483..ebace6cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ file(GLOB_RECURSE LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/json/json_parser.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/tx_metadata.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/script_parser.c app/src/base32.c app/src/crypto.c ) diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index dc1e7156..07ec6d2b 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -33,6 +33,7 @@ #include "hdpath.h" #include "parser_impl.h" #include "message.h" +#include "script_parser.h" __Z_INLINE void handleGetPubkey(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { hasPubkey = false; @@ -83,50 +84,56 @@ __Z_INLINE void handleGetPubkey(volatile uint32_t *flags, volatile uint32_t *tx, __Z_INLINE void handleSign(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { process_chunk_response_t callType = process_chunk(tx, rx); - if (callType == PROCESS_CHUNK_NOT_FINISHED) { - THROW(APDU_CODE_OK); - } - if (callType == PROCESS_CHUNK_FINISHED_MESSAGE) { - zxerr_t err = message_parse(); - if (err != zxerr_ok) { - const char *error_msg = "Invalid message"; - int error_msg_length = strlen(error_msg); - MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length); - *tx += (error_msg_length); - THROW(APDU_CODE_DATA_INVALID); - } - CHECK_APP_CANARY() - view_review_init(message_getItem, message_getNumItems, app_sign_message); - view_review_show(); - *flags |= IO_ASYNCH_REPLY; - } - - const char *error_msg = tx_parse(callType); - - if (error_msg != NULL) { - int error_msg_length = strlen(error_msg); - MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length); - *tx += (error_msg_length); - THROW(APDU_CODE_DATA_INVALID); - } + switch (callType) { + case PROCESS_CHUNK_NOT_FINISHED: + THROW(APDU_CODE_OK); + case PROCESS_CHUNK_FINISHED_MESSAGE:; + zxerr_t err = message_parse(); + if (err != zxerr_ok) { + const char *error_msg = "Invalid message"; + int error_msg_length = strlen(error_msg); + MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length); + *tx += (error_msg_length); + THROW(APDU_CODE_DATA_INVALID); + } + CHECK_APP_CANARY() + view_review_init(message_getItem, message_getNumItems, app_sign_message); + view_review_show(); + *flags |= IO_ASYNCH_REPLY; + break; + case PROCESS_CHUNK_FINISHED_NFT1: + case PROCESS_CHUNK_FINISHED_NFT2: + case PROCESS_CHUNK_FINISHED_NO_METADATA: + case PROCESS_CHUNK_FINISHED_WITH_METADATA: ; + const char *error_msg = tx_parse(callType); + + if (error_msg != NULL) { + int error_msg_length = strlen(error_msg); + MEMCPY(G_io_apdu_buffer, error_msg, error_msg_length); + *tx += (error_msg_length); + THROW(APDU_CODE_DATA_INVALID); + } + show_address = SHOW_ADDRESS_NONE; + loadAddressCompareHdPathFromSlot(); + + //if we found matching hdPath on slot 0 + if (show_address == SHOW_ADDRESS_YES || show_address == SHOW_ADDRESS_YES_HASH_MISMATCH) { + checkAddressUsedInTx(); + } + else { + addressUsedInTx = 0; + } - show_address = SHOW_ADDRESS_NONE; - loadAddressCompareHdPathFromSlot(); - - //if we found matching hdPath on slot 0 - if (show_address == SHOW_ADDRESS_YES || show_address == SHOW_ADDRESS_YES_HASH_MISMATCH) { - checkAddressUsedInTx(); + CHECK_APP_CANARY() + view_review_init(tx_getItem, tx_getNumItems, app_sign); + view_review_show(); + *flags |= IO_ASYNCH_REPLY; + break; + default: + THROW(APDU_CODE_UNKNOWN); } - else { - addressUsedInTx = 0; - } - - CHECK_APP_CANARY() - view_review_init(tx_getItem, tx_getNumItems, app_sign); - view_review_show(); - *flags |= IO_ASYNCH_REPLY; } __Z_INLINE void handleSlotStatus(__Z_UNUSED volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { diff --git a/app/src/common/app_main.c b/app/src/common/app_main.c index 5174ce83..ebfbc2b6 100644 --- a/app/src/common/app_main.c +++ b/app/src/common/app_main.c @@ -116,8 +116,9 @@ void extractHDPathAndCryptoOptions(uint32_t rx, uint32_t offset) { process_chunk_response_t process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_t rx) { const uint8_t payloadType = G_io_apdu_buffer[OFFSET_PAYLOAD_TYPE]; + const uint8_t p2 = G_io_apdu_buffer[OFFSET_P2]; - if (G_io_apdu_buffer[OFFSET_P2] != 0) { + if (p2 != 0 && payloadType != 0x02) { THROW(APDU_CODE_INVALIDP1P2); } @@ -127,7 +128,7 @@ process_chunk_response_t process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_ uint32_t added; switch (payloadType) { - case 0: + case 0x00: tx_initialize(); tx_reset(); extractHDPathAndCryptoOptions(rx, OFFSET_DATA); @@ -144,7 +145,16 @@ process_chunk_response_t process_chunk(__Z_UNUSED volatile uint32_t *tx, uint32_ if (added != rx - OFFSET_DATA) { THROW(APDU_CODE_OUTPUT_BUFFER_TOO_SMALL); } - return PROCESS_CHUNK_FINISHED_NO_METADATA; + switch (p2) { + case 0x01: + return PROCESS_CHUNK_FINISHED_NO_METADATA; + case 0x02: + return PROCESS_CHUNK_FINISHED_NFT1; + case 0x03: + return PROCESS_CHUNK_FINISHED_NFT2; + default: + THROW(APDU_CODE_INVALIDP1P2); + } case 0x03: if (storeTxMetadata(&(G_io_apdu_buffer[OFFSET_DATA]), rx - OFFSET_DATA) != PARSER_OK) { initStoredTxMetadata(); //delete merkle tree proof on error for redundant security diff --git a/app/src/common/app_main.h b/app/src/common/app_main.h index 9f9fcbb0..6ad96323 100644 --- a/app/src/common/app_main.h +++ b/app/src/common/app_main.h @@ -49,6 +49,8 @@ typedef enum { PROCESS_CHUNK_NOT_FINISHED = 0, PROCESS_CHUNK_FINISHED_WITH_METADATA, PROCESS_CHUNK_FINISHED_NO_METADATA, + PROCESS_CHUNK_FINISHED_NFT1, + PROCESS_CHUNK_FINISHED_NFT2, PROCESS_CHUNK_FINISHED_MESSAGE, } process_chunk_response_t; diff --git a/app/src/common/tx.c b/app/src/common/tx.c index 9305c93f..dc6b5594 100644 --- a/app/src/common/tx.c +++ b/app/src/common/tx.c @@ -75,11 +75,14 @@ uint8_t *tx_get_buffer() { } const char *tx_parse(process_chunk_response_t typeOfCall) { + script_parsed_type_t scriptType = (typeOfCall == PROCESS_CHUNK_FINISHED_NFT1) ? SCRIPT_TYPE_NFT_SETUP_COLLECTION : + (typeOfCall == PROCESS_CHUNK_FINISHED_NFT2) ? SCRIPT_TYPE_NFT_TRANSFER : SCRIPT_TYPE_UNKNOWN; // parse tx uint8_t err = parser_parse( &ctx_parsed_tx, tx_get_buffer(), - tx_get_buffer_length()); + tx_get_buffer_length(), + scriptType); if (err != PARSER_OK) { return parser_getErrorDescription(err); @@ -87,19 +90,25 @@ const char *tx_parse(process_chunk_response_t typeOfCall) { //parse metadata parser_tx_obj.metadataInitialized = false; - if (typeOfCall == PROCESS_CHUNK_FINISHED_WITH_METADATA) { - MEMZERO(&parser_tx_obj.metadata, sizeof(parser_tx_obj.metadata)); - err = parseTxMetadata(parser_tx_obj.hash.digest, &parser_tx_obj.metadata); - if (err != PARSER_OK) { - return parser_getErrorDescription(err); - } - parser_tx_obj.metadataInitialized = true; - } - else { - //If no metadata provided = script is not known, expert mode must be on - if (!app_mode_expert()) { - return parser_getErrorDescription(PARSER_UNEXPECTED_SCRIPT); - } + switch (typeOfCall) { + case PROCESS_CHUNK_FINISHED_WITH_METADATA: + MEMZERO(&parser_tx_obj.metadata, sizeof(parser_tx_obj.metadata)); + err = parseTxMetadata(parser_tx_obj.hash.digest, &parser_tx_obj.metadata); + if (err != PARSER_OK) { + return parser_getErrorDescription(err); + } + parser_tx_obj.metadataInitialized = true; + break; + case PROCESS_CHUNK_FINISHED_NFT1: + case PROCESS_CHUNK_FINISHED_NFT2: + break; // we do not need metadata for these scripts + case PROCESS_CHUNK_FINISHED_NO_METADATA: + if (!app_mode_expert()) { // we do not need metadata for these scripts, but this workflow should work only in expert mode + return parser_getErrorDescription(PARSER_UNEXPECTED_SCRIPT); + } + break; + default: + return parser_getErrorDescription(PARSER_UNEXPECTED_ERROR); } CHECK_APP_CANARY() diff --git a/app/src/parser.c b/app/src/parser.c index 474b5ea6..11071b9b 100644 --- a/app/src/parser.c +++ b/app/src/parser.c @@ -35,9 +35,9 @@ #define MAX_JSON_ARRAY_TOKEN_COUNT 64 -parser_error_t parser_parse(parser_context_t *ctx, const uint8_t *data, size_t dataLen) { +parser_error_t parser_parse(parser_context_t *ctx, const uint8_t *data, size_t dataLen, script_parsed_type_t scriptType) { CHECK_PARSER_ERR(parser_init(ctx, data, dataLen)) - return _read(ctx, &parser_tx_obj); + return _read(ctx, &parser_tx_obj, scriptType); } parser_error_t parser_validate(const parser_context_t *ctx) { @@ -624,15 +624,182 @@ parser_error_t parser_getItem_internal(int8_t *displayIdx, } } else { - SCREEN(true) { - snprintf(outKey, outKeyLen, "Script hash"); - pageHexString(outVal, outValLen, parser_tx_obj.hash.digest, sizeof(parser_tx_obj.hash.digest), pageIdx, pageCount); - return PARSER_OK; - } - SCREEN(true) { - snprintf(outKey, outKeyLen, "Verify script hash"); - snprintf(outVal, outValLen, "on a secure device."); - return PARSER_OK; + const char storageString[] = "/storage/"; + const char publicString[] = "/public/"; + const size_t storageStringLength = sizeof(storageString)-1; + const size_t publicStringLength = sizeof(publicString)-1; + switch (parser_tx_obj.parsedScript.script_type) { + case SCRIPT_TYPE_NFT_SETUP_COLLECTION: { + //validate basic assumptions on the script + if (parser_tx_obj.parsedScript.elements_count != PARSED_ELEMENTS_NFT1_COUNT) { + return PARSER_UNEXPECTED_ERROR; + } + // storagePath should be preceeded with "/storage/" - double checking before displaying it + STATIC_ASSERT(PARSED_ELEMENTS_NFT1_STORAGE_PATH >= 1, "Script index error"); + bool enoughSpaceForStorageString = (parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_STORAGE_PATH].data + >= parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_STORAGE_PATH-1].data + storageStringLength); + if (!enoughSpaceForStorageString) { + return PARSER_UNEXPECTED_ERROR; + } + if (memcmp(parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_STORAGE_PATH].data - storageStringLength, + storageString, storageStringLength)) { + return PARSER_UNEXPECTED_ERROR; + } + // publicCollectionContractName and publicCollectionName should be separated by a single '.' - double checking before displaying it + STATIC_ASSERT(PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME + 1 == PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_NAME, + "Script index error"); + if (parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME].data + + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME].length + 1 != + parser_tx_obj.parsedScript.elements[8].data) { + return PARSER_UNEXPECTED_ERROR; + } + size_t expectedDotIndex = parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME].length; + if (parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME].data[expectedDotIndex] != '.') { + return PARSER_UNEXPECTED_ERROR; + } + + // publicPath should be preceeded with "/public/" - double checking before displaying it + STATIC_ASSERT(PARSED_ELEMENTS_NFT1_PUBLIC_PATH >= 1, "Script index error"); + bool enoughSpaceForPublicString = (parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_PATH].data + >= parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_PATH-1].data + publicStringLength); + if (!enoughSpaceForPublicString) { + return PARSER_UNEXPECTED_ERROR; + } + if (memcmp(parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_PATH].data - publicStringLength, + publicString, publicStringLength)) { + return PARSER_UNEXPECTED_ERROR; + } + + SCREEN(true) { + snprintf(outKey, outKeyLen, "Set Up NFT"); + snprintf(outVal, outValLen, "Collection"); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Contract Name"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_CONTRACT_NAME].data, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_CONTRACT_NAME].length, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Contract Address"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_CONTRACT_ADDRESS].data, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_CONTRACT_ADDRESS].length, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Storage Path"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_STORAGE_PATH].data - storageStringLength, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_STORAGE_PATH].length + storageStringLength, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Public Coll. Name"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME].data, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME].length + + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_NAME].length + 1, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Public Path"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_PATH].data - publicStringLength, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT1_PUBLIC_PATH].length + publicStringLength, + pageIdx, pageCount); + return PARSER_OK; + } + break; + } + case SCRIPT_TYPE_NFT_TRANSFER: { + //validate basic assumptions on the script + if (parser_tx_obj.parsedScript.elements_count != PARSED_ELEMENTS_NFT2_COUNT) { + return PARSER_UNEXPECTED_ERROR; + } + // storagePath should be preceeded with "/storage/" - double checking before displaying it + STATIC_ASSERT(PARSED_ELEMENTS_NFT2_STORAGE_PATH >= 1, "Script index error"); + bool enoughSpaceForStorageString = (parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_STORAGE_PATH].data + >= parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_STORAGE_PATH-1].data + storageStringLength); + if (!enoughSpaceForStorageString) { + return PARSER_UNEXPECTED_ERROR; + } + if (memcmp(parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_STORAGE_PATH].data - storageStringLength, + storageString, storageStringLength)) { + return PARSER_UNEXPECTED_ERROR; + } + // publicPath should be preceeded with "/public/" - double checking before displaying it + STATIC_ASSERT(PARSED_ELEMENTS_NFT2_PUBLIC_PATH >= 1, "Script index error"); + bool enoughSpaceForPublicString = (parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_PUBLIC_PATH].data + >= parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_PUBLIC_PATH-1].data + publicStringLength); + if (!enoughSpaceForPublicString) { + return PARSER_UNEXPECTED_ERROR; + } + if (memcmp(parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_PUBLIC_PATH].data - publicStringLength, + publicString, publicStringLength)) { + return PARSER_UNEXPECTED_ERROR; + } + + SCREEN(true) { + snprintf(outKey, outKeyLen, "Transfer NFT"); + snprintf(outVal, outValLen, ""); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Contract Name"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_CONTRACT_NAME].data, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_CONTRACT_NAME].length, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Contract Address"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_CONTRACT_ADDRESS].data, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_CONTRACT_ADDRESS].length, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Storage Path"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_STORAGE_PATH].data - storageStringLength, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_STORAGE_PATH].length + storageStringLength, + pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Public Path"); + pageStringExt(outVal, outValLen, + (const char *) parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_PUBLIC_PATH].data - publicStringLength, + parser_tx_obj.parsedScript.elements[PARSED_ELEMENTS_NFT2_PUBLIC_PATH].length + publicStringLength, + pageIdx, pageCount); + return PARSER_OK; + } + break; + } + case SCRIPT_TYPE_UNKNOWN: { + SCREEN(true) { + snprintf(outKey, outKeyLen, "Script hash"); + pageHexString(outVal, outValLen, parser_tx_obj.hash.digest, sizeof(parser_tx_obj.hash.digest), pageIdx, pageCount); + return PARSER_OK; + } + SCREEN(true) { + snprintf(outKey, outKeyLen, "Verify script hash"); + snprintf(outVal, outValLen, "on a secure device."); + return PARSER_OK; + } + break; + } + default: + return PARSER_UNEXPECTED_ERROR; } } @@ -699,28 +866,56 @@ parser_error_t parser_getItem_internal(int8_t *displayIdx, } } else { // No metadata - SCREEN(true) { - snprintf(outKey, outKeyLen, "Script arguments"); - snprintf(outVal, outValLen, "Number of arguments: %d", parser_tx_obj.arguments.argCount); - return PARSER_OK; - } - for(size_t i=0; idigest, sizeof(s->digest)); sha256(script.buffer, script.bufferLen, s->digest); + MEMZERO(e, sizeof(*e)); + e->script_type = SCRIPT_TYPE_UNKNOWN; + switch (scriptType) { + case SCRIPT_TYPE_NFT_SETUP_COLLECTION: + if (!parseNFT1(e, script.buffer, script.bufferLen)) { + return PARSER_UNEXPECTED_SCRIPT; + } + break; + case SCRIPT_TYPE_NFT_TRANSFER: + if (!parseNFT2(e, script.buffer, script.bufferLen)) { + return PARSER_UNEXPECTED_SCRIPT; + } + break; + case SCRIPT_TYPE_UNKNOWN: + break; + default: + return PARSER_UNEXPECTED_ERROR; + } + return PARSER_OK; } @@ -540,7 +559,7 @@ parser_error_t _readProposalAuthorizers(parser_context_t *c, flow_proposal_autho return PARSER_OK; } -parser_error_t _read(parser_context_t *c, parser_tx_t *v) { +parser_error_t _read(parser_context_t *c, parser_tx_t *v, script_parsed_type_t scriptType) { rlp_kind_e kind; uint32_t bytesConsumed; @@ -563,7 +582,7 @@ parser_error_t _read(parser_context_t *c, parser_tx_t *v) { // Go through the inner list - CHECK_PARSER_ERR(_readScript(&ctx_rootInnerList, &v->hash)) + CHECK_PARSER_ERR(_readScript(&ctx_rootInnerList, &v->hash, &v->parsedScript, scriptType)) CHECK_PARSER_ERR(_readArguments(&ctx_rootInnerList, &v->arguments)) CHECK_PARSER_ERR(_readReferenceBlockId(&ctx_rootInnerList, &v->referenceBlockId)) CHECK_PARSER_ERR(_readGasLimit(&ctx_rootInnerList, &v->gasLimit)) diff --git a/app/src/parser_impl.h b/app/src/parser_impl.h index a10aa2fa..83297092 100644 --- a/app/src/parser_impl.h +++ b/app/src/parser_impl.h @@ -30,7 +30,7 @@ extern parser_tx_t parser_tx_obj; parser_error_t parser_init(parser_context_t *ctx, const uint8_t *buffer, uint16_t bufferSize); -parser_error_t _read(parser_context_t *c, parser_tx_t *v); +parser_error_t _read(parser_context_t *c, parser_tx_t *v, script_parsed_type_t scriptType); parser_error_t _validateTx(const parser_context_t *c, const parser_tx_t *v); diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index 6dbd18cb..df623236 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -28,6 +28,7 @@ extern "C" { #include #include #include "crypto.h" +#include "script_parser.h" typedef enum { CHAIN_ID_UNKNOWN, @@ -77,6 +78,7 @@ typedef struct { typedef struct { bool metadataInitialized; parsed_tx_metadata_t metadata; + script_parsed_elements_t parsedScript; flow_script_hash_t hash; flow_argument_list_t arguments; flow_reference_block_id_t referenceBlockId; diff --git a/app/src/script_parser.c b/app/src/script_parser.c new file mode 100644 index 00000000..624782f5 --- /dev/null +++ b/app/src/script_parser.c @@ -0,0 +1,193 @@ +#include "script_parser.h" + + +static bool isElementChar(uint8_t scriptChar) { + return ('a'<=scriptChar && scriptChar<='z') || + ('A'<=scriptChar && scriptChar<='Z') || + ('0'<=scriptChar && scriptChar<='9') || + ('_'==scriptChar); +} + +//returns parsed element length, 0 indicates error +static size_t parseElement(size_t index, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize) { + size_t elementLength = 0; + while (index + elementLength < scriptToParseSize) { + uint8_t scriptChar = scriptToParse[index + elementLength]; + if (!isElementChar(scriptChar)) { + return elementLength; + } + elementLength += 1; + } + return elementLength; +} + + +bool parseScript(script_parsed_elements_t *parsedElements, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize, + const uint8_t *scriptTemplate, size_t scriptTemplateSize) { + parsedElements->elements_count = 0; + size_t scriptRead = 0; + size_t templateRead = 0; + while (templateRead < scriptTemplateSize) { + uint8_t templateChar = scriptTemplate[templateRead]; + + if (templateChar == 1) { //token to parse + size_t elementLen = parseElement(scriptRead, scriptToParse, scriptToParseSize); + if (elementLen == 0 || parsedElements->elements_count == MAX_SCRIPT_PARSED_ELEMENTS) { + return false; + } + parsedElements->elements[parsedElements->elements_count].data = scriptToParse + scriptRead; + parsedElements->elements[parsedElements->elements_count].length = elementLen; + + parsedElements->elements_count += 1; + scriptRead += elementLen; + templateRead += 1; + } + else { //character to match one in the script + if ((scriptRead >= scriptToParseSize) || (templateChar != scriptToParse[scriptRead])) { + return false; + } + scriptRead += 1; + templateRead += 1; + } + } + + if (scriptRead != scriptToParseSize || templateRead != scriptTemplateSize) { + return false; + } + + parsedElements->script_type = SCRIPT_TYPE_UNKNOWN; + return true; +} + + +#define ELEMENTS_MUST_BE_EQUAL(pE, i, j) { \ + if ((i >= pE->elements_count) || (j >= pE->elements_count)) return false; \ + if (pE->elements[i].length != pE->elements[j].length) return false; \ + if (MEMCMP(pE->elements[i].data, pE->elements[j].data, pE->elements[i].length)) return false; \ +} + +#define ADDRESS_STRING_LENGTH 18 +#define NONFUNGIBLETOKEN_METADATAVIEWS_TESTNET "0x631e88ae7f1d7c20" +#define NONFUNGIBLETOKEN_METADATAVIEWS_MAINNET "0x1d7e57aa55817448" + +#define ELEMENT_MUST_BE_ADDRESS(pE, i) { \ + STATIC_ASSERT(sizeof(NONFUNGIBLETOKEN_METADATAVIEWS_TESTNET) == ADDRESS_STRING_LENGTH + 1, "Incompatible types"); \ + STATIC_ASSERT(sizeof(NONFUNGIBLETOKEN_METADATAVIEWS_MAINNET) == ADDRESS_STRING_LENGTH + 1, "Incompatible types"); \ + if (i >= pE->elements_count) return false; \ + if (pE->elements[i].length != ADDRESS_STRING_LENGTH) return false; \ + if (MEMCMP(pE->elements[i].data, NONFUNGIBLETOKEN_METADATAVIEWS_TESTNET, pE->elements[i].length) && \ + MEMCMP(pE->elements[i].data, NONFUNGIBLETOKEN_METADATAVIEWS_MAINNET, pE->elements[i].length)) return false; \ +} + + +// Elements (see enum in script_parser.h) : +// 0 - NonFungibleToken address +// 1 - MetadataViews address +// 2 - contractName +// 3 - contractAddress +// 4 - storagePath +// 5 - contractName +// 6 - storagePath +// 7 - publicCollectionContractName +// 8 - publicCollectionName +// 9 - publicPath +// 10 - storagePath +bool parseNFT1(script_parsed_elements_t *parsedElements, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize) { + const char template[] = + "import NonFungibleToken from \001\n" + "import MetadataViews from \001\n" + "import \001 from \001\n" + "transaction {\n" + " prepare(acct: AuthAccount) {\n" + " let collectionType = acct.type(at: /storage/\001)\n" + " // if there already is a collection stored, return\n" + " if (collectionType != nil) {\n" + " return\n" + " }\n" + " // create empty collection\n" + " let collection <- \001.createEmptyCollection()\n" + " // put the new Collection in storage\n" + " acct.save(<-collection, to: /storage/\001)\n" + " // create a public capability for the collection\n" + " acct.link<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, \001.\001, MetadataViews.ResolverCollection}>(\n" + " /public/\001,\n" + " target: /storage/\001\n" + " )\n" + " }\n" + "}\n"; + + if(!parseScript(parsedElements, scriptToParse, scriptToParseSize, (const uint8_t *) template, sizeof(template)-1)) { // -1 to strip terminating 0 + return false; + } + + ELEMENT_MUST_BE_ADDRESS(parsedElements, PARSED_ELEMENTS_NFT1_NON_FUNGIBLE_TOKEN_ADDRESS); + ELEMENT_MUST_BE_ADDRESS(parsedElements, PARSED_ELEMENTS_NFT1_METADATA_VIEWS_ADDRESS); + + ELEMENTS_MUST_BE_EQUAL(parsedElements, PARSED_ELEMENTS_NFT1_CONTRACT_NAME, PARSED_ELEMENTS_NFT1_CONTRACT_NAME2); + ELEMENTS_MUST_BE_EQUAL(parsedElements, PARSED_ELEMENTS_NFT1_STORAGE_PATH, PARSED_ELEMENTS_NFT1_STORAGE_PATH2); + ELEMENTS_MUST_BE_EQUAL(parsedElements, PARSED_ELEMENTS_NFT1_STORAGE_PATH, PARSED_ELEMENTS_NFT1_STORAGE_PATH3); + + parsedElements->script_type = SCRIPT_TYPE_NFT_SETUP_COLLECTION; + return true; +} + + +// Elements (see enum in script_parser.h) : +// 0 - NonFungibleToken address +// 1 - contractName +// 2 - contractAddress +// 3 - storagePath +// 4 - contractName +// 5 - contractName +// 6 - storagePath +// 7 - publicPath +bool parseNFT2(script_parsed_elements_t *parsedElements, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize) { + const char template[] = + "import NonFungibleToken from \001\n" + "import \001 from \001\n" + "transaction(recipient: Address, withdrawID: UInt64) {\n" + " // local variable for storing the transferred nft\n" + " let transferToken: @NonFungibleToken.NFT\n" + " prepare(owner: AuthAccount) {\n" + " // check if collection exists\n" + " if (owner.type(at: /storage/\001) != Type<@\001.Collection>()) {\n" + " panic(\"Could not borrow a reference to the stored collection\")\n" + " }\n" + " // borrow a reference to the collection\n" + " let collectionRef = owner\n" + " .borrow<&\001.Collection>(from: /storage/\001)!\n" + " // withdraw the NFT\n" + " self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID)\n" + " }\n" + " execute {\n" + " // get the recipient's public account object\n" + " let recipient = getAccount(recipient)\n" + " // get receivers capability\n" + " let nonFungibleTokenCapability = recipient\n" + " .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/\001)\n" + " // check the recipient has a NonFungibleToken public capability\n" + " if (!nonFungibleTokenCapability.check()) {\n" + " panic(\"Could not borrow a reference to the receiver's collection\")\n" + " }\n" + " // deposit nft to recipients collection\n" + " nonFungibleTokenCapability\n" + " .borrow()!\n" + " .deposit(token: <-self.transferToken)\n" + " }\n" + "}\n"; + if(!parseScript(parsedElements, scriptToParse, scriptToParseSize, (const uint8_t *) template, sizeof(template)-1)) { // -1 to strip terminating 0 + return false; + } + + ELEMENT_MUST_BE_ADDRESS(parsedElements, PARSED_ELEMENTS_NFT2_NON_FUNGIBLE_TOKEN_ADDRESS); + + ELEMENTS_MUST_BE_EQUAL(parsedElements, PARSED_ELEMENTS_NFT2_CONTRACT_NAME, PARSED_ELEMENTS_NFT2_CONTRACT_NAME2); + ELEMENTS_MUST_BE_EQUAL(parsedElements, PARSED_ELEMENTS_NFT2_CONTRACT_NAME, PARSED_ELEMENTS_NFT2_CONTRACT_NAME3); + ELEMENTS_MUST_BE_EQUAL(parsedElements, PARSED_ELEMENTS_NFT2_STORAGE_PATH, PARSED_ELEMENTS_NFT2_STORAGE_PATH2); + + parsedElements->script_type = SCRIPT_TYPE_NFT_TRANSFER; + return true; +} + +#undef ELEMENTS_MUST_BE_EQUAL +#undef ELEMENT_MUST_BE_ADDRESS \ No newline at end of file diff --git a/app/src/script_parser.h b/app/src/script_parser.h new file mode 100644 index 00000000..42424ebc --- /dev/null +++ b/app/src/script_parser.h @@ -0,0 +1,69 @@ +#include +#include + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_SCRIPT_PARSED_ELEMENTS 11 + +typedef enum { + SCRIPT_TYPE_UNKNOWN = 0x00, + SCRIPT_TYPE_NFT_SETUP_COLLECTION = 0x01, + SCRIPT_TYPE_NFT_TRANSFER = 0x02, +} script_parsed_type_t; + +typedef struct { + const NV_VOLATILE uint8_t *data; + size_t length; +} script_element_t; + +typedef struct { + script_parsed_type_t script_type; + size_t elements_count; + script_element_t elements[MAX_SCRIPT_PARSED_ELEMENTS]; +} script_parsed_elements_t; + +bool parseScript(script_parsed_elements_t *parsedElements, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize, + const uint8_t *scriptTemplate, size_t scriptTemplateSize); + +typedef enum { + PARSED_ELEMENTS_NFT1_NON_FUNGIBLE_TOKEN_ADDRESS = 0, + PARSED_ELEMENTS_NFT1_METADATA_VIEWS_ADDRESS = 1, + PARSED_ELEMENTS_NFT1_CONTRACT_NAME = 2, + PARSED_ELEMENTS_NFT1_CONTRACT_ADDRESS = 3, + PARSED_ELEMENTS_NFT1_STORAGE_PATH = 4, + PARSED_ELEMENTS_NFT1_CONTRACT_NAME2 = 5, + PARSED_ELEMENTS_NFT1_STORAGE_PATH2 = 6, + PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_CONTRACT_NAME = 7, + PARSED_ELEMENTS_NFT1_PUBLIC_COLLECTION_NAME = 8, + PARSED_ELEMENTS_NFT1_PUBLIC_PATH = 9, + PARSED_ELEMENTS_NFT1_STORAGE_PATH3 = 10, + + PARSED_ELEMENTS_NFT1_COUNT = 11, +} parsed_elements_nft1_index_t; + + +bool parseNFT1(script_parsed_elements_t *parsedElements, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize); + +typedef enum { + PARSED_ELEMENTS_NFT2_NON_FUNGIBLE_TOKEN_ADDRESS = 0, + PARSED_ELEMENTS_NFT2_CONTRACT_NAME = 1, + PARSED_ELEMENTS_NFT2_CONTRACT_ADDRESS = 2, + PARSED_ELEMENTS_NFT2_STORAGE_PATH = 3, + PARSED_ELEMENTS_NFT2_CONTRACT_NAME2 = 4, + PARSED_ELEMENTS_NFT2_CONTRACT_NAME3 = 5, + PARSED_ELEMENTS_NFT2_STORAGE_PATH2 = 6, + PARSED_ELEMENTS_NFT2_PUBLIC_PATH = 7, + + PARSED_ELEMENTS_NFT2_COUNT = 8, +} parsed_elements_nft2_index_t; + +bool parseNFT2(script_parsed_elements_t *parsedElements, const uint8_t NV_VOLATILE *scriptToParse, size_t scriptToParseSize); + + +#ifdef __cplusplus +} //end extern C +#endif diff --git a/app/src/tx_metadata.h b/app/src/tx_metadata.h index 3a1351c6..a2e366c0 100644 --- a/app/src/tx_metadata.h +++ b/app/src/tx_metadata.h @@ -32,7 +32,7 @@ typedef enum { ARGUMENT_TYPE_OPTIONALARRAY = 4 } argument_type_e; -#define PARSER_MAX_ARGCOUNT 10 +#define PARSER_MAX_ARGCOUNT 9 #define METADATA_HASH_SIZE 32 //CX_SHA256_SIZE #define MAX_METADATA_MAX_ARRAY_ITEMS 20 diff --git a/deps/ledger-zxlib/dockerized_build.mk b/deps/ledger-zxlib/dockerized_build.mk index d3f9e939..3800d947 100644 --- a/deps/ledger-zxlib/dockerized_build.mk +++ b/deps/ledger-zxlib/dockerized_build.mk @@ -346,6 +346,7 @@ speculos_port_5002_test_internal: $(call run_nodejs_test,5002,40002,test-slot-transaction-interaction.js) $(call run_nodejs_test,5002,40002,test-transaction-expert-mode.js) $(call run_nodejs_test,5002,40002,test-transaction-arbitrary.js) + $(call run_nodejs_test,5002,40002,test-transaction-nft.js) $(call run_nodejs_test,5002,40002,test-messages.js) $(call run_nodejs_test,5002,40002,test-transactions.js) @echo "# ALL TESTS COMPLETED!" | tee -a $(TESTS_SPECULOS_DIR)/speculos-port-5002.log diff --git a/docs/APDUSPEC.md b/docs/APDUSPEC.md index 413315d8..411fba05 100644 --- a/docs/APDUSPEC.md +++ b/docs/APDUSPEC.md @@ -151,29 +151,35 @@ Each slot has the following structure #### Command -| Field | Type | Content | Expected | -| ----- | -------- | ---------------------- | ------------- | -| CLA | byte (1) | Application Identifier | 0x33 | -| INS | byte (1) | Instruction ID | 0x02 | -| P1 | byte (1) | Payload desc | 0 = init | -| | | | 1 = add | -| | | | 3 = template | -| | | | 4,5 = hashes | -| P2 | byte (1) | ---- | not used | -| L | byte (1) | Bytes in payload | (depends) | +| Field | Type | Content | Expected | +| ----- | -------- | ---------------------- | ------------------ | +| CLA | byte (1) | Application Identifier | 0x33 | +| INS | byte (1) | Instruction ID | 0x02 | +| P1 | byte (1) | Payload desc | 0 = init | +| | | | 1 = add | +| | | | 2 = final | +| | | | 3 = metadata | +| | | | 4 = MT proof | +| | | | 5 = MT proof final | +| | | | 10 = message final | +| P2 | byte (1) | ---- | (depends) | +| L | byte (1) | Bytes in payload | (depends) | The first packet/chunk includes only the derivation path -All other packets/chunks contain data chunks that are described below. There are three workflows as of now (typical sequences here, the app allows other combination of commands, too): +All other packets/chunks contain data chunks that are described below. There are following workflows as of now (typical sequences here, the app allows other combination of commands, too): Merkle tree workflow - Init packet, several add packets, metadata packet, four Merkle tree packets (3x 0x04 and finaly 0x05). Arbitrary transaction signing - Init packet, several add packets, final packet. +NFT workflow - Init packet, several add packets, final packet. Message signing workflow - Init packet, several add packets, final message packet (P1=0x10). ##### Init Packet P1 = 0x00 | Field | Type | Content | Expected | | ------- | -------- | -------------------- | -------- | +| P2 | byte (1) | | not used | +| ------- | -------- | -------------------- | -------- | | Path[0] | byte (4) | Derivation Path Data | 44' | | Path[1] | byte (4) | Derivation Path Data | 539' | | Path[2] | byte (4) | Derivation Path Data | ? | @@ -185,9 +191,11 @@ This clears data and sets detivation path and crypto options variable ##### Add Packet P1 = 0x01 -| Field | Type | Content | Expected | -| ----- | -------- | ------- | -------- | -| Data | bytes... | Message | | +| Field | Type | Content | Expected | +| ----- | -------- | ---------- | -------- | +| P2 | byte (1) | | not used | +| ----- | -------- | ---------- | -------- | +| Data | bytes... | see bellow | | Data is defined as: @@ -197,11 +205,16 @@ Data is defined as: Appends to data (transaction or message) -##### Fimal Packet P1 = 0x02 +##### Final Packet P1 = 0x02 -| Field | Type | Content | Expected | -| ----- | -------- | ------- | -------- | -| Data | bytes... | Message | | +| Field | Type | Content | Expected | +| ----- | -------- | ---------- | ---------- | +| P2 | byte (1) | workflow | 1, 2, or 3 | +| ----- | -------- | ---------- | ---------- | +| Data | bytes... | see bellow | | + +Workflow is defined as: 1 - Arbitrary message signing, 2 - Setup NFT collection, 3 - Transfer NFT +Arbitrary message signing requires expert mode and is able to handle any transaction. Setup NFT collection and Transfer NFT require the transaction to contain script (and arguments) conforming to NFT script templates (see script_parser.c). Data is defined as: @@ -215,6 +228,8 @@ Appends to transaction data and initiates transaction signing without metadata ( | Field | Type | Content | Expected | | ----- | -------- | -------- | -------- | +| P2 | byte (1) | | not used | +| ----- | -------- | -------- | -------- | | Data | bytes... | Metadata | | Metadata is defined as: @@ -265,6 +280,8 @@ Four APDUs for four levels of internal merkle tree nodes. Each internal nerkle t | Field | Type | Content | Expected | | ------------------- | ------------ | ---------------- | -------- | +| P2 | byte (1) | | not used | +| ------------------- | ------------ | ---------------- | -------- | | Merkle tree hash 1 | byte (32) | Merkle tree hash | | | Merkle tree hash 2 | byte (32) | Merkle tree hash | | | ... | | | | @@ -276,6 +293,8 @@ Validates merkle tree node. Validates that previous hash (metadata hash or merkl | Field | Type | Content | Expected | | ----- | -------- | ------- | -------- | +| P2 | byte (1) | | not used | +| ----- | -------- | ------- | -------- | | Data | bytes... | Message | | Data is defined as: diff --git a/fuzz/parser_parse.cpp b/fuzz/parser_parse.cpp index 0acb3ab3..db6eb660 100644 --- a/fuzz/parser_parse.cpp +++ b/fuzz/parser_parse.cpp @@ -22,7 +22,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) parser_context_t ctx; parser_error_t rc; - rc = parser_parse(&ctx, data, size); + rc = parser_parse(&ctx, data, size, SCRIPT_TYPE_UNKNOWN); if (rc != PARSER_OK) { return 0; } diff --git a/js/src/index.js b/js/src/index.js index 5d3764a8..7ee6926a 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -177,11 +177,12 @@ export default class FlowApp { for (let i = 0; i < chunks.length; i += 1) { const payloadType = chunks[i].type + const p2 = chunks[i].p2 const chunk = chunks[i].buffer // eslint-disable-next-line no-await-in-loop result = await this.transport - .send(CLA, INS.SIGN, payloadType, 0, chunk, [0x9000, 0x6984, 0x6a80]) + .send(CLA, INS.SIGN, payloadType, p2, chunk, [0x9000, 0x6984, 0x6a80]) .then((response) => { const errorCodeData = response.slice(-2); const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; diff --git a/js/src/signTransaction.js b/js/src/signTransaction.js index 1fdd3e8a..0c870a87 100644 --- a/js/src/signTransaction.js +++ b/js/src/signTransaction.js @@ -12,6 +12,14 @@ const PAYLOAD_TYPE = { MESSAGE_LAST: 0x10, } +const P2_UNUSED = 0x00; + +const PAYLOAD_TYPE_LAST_P2 = { + ARBITRARY_MESSAGE_SIGNING: 0x01, + NFT_SETUP_COLLECTION: 0x02, + NFT_TRANSFER: 0x03, +} + export function signIsLastAPDU(type) { return (type === PAYLOAD_TYPE.LAST || type === PAYLOAD_TYPE.MERKLE_TREE_LAST || PAYLOAD_TYPE.MESSAGE_LAST) } @@ -23,7 +31,7 @@ function prepareBasicChunks(serializedPathBuffer, message) { const chunks = []; // First chunk (only path) - chunks.push({type: PAYLOAD_TYPE.INIT, buffer: serializedPathBuffer}); + chunks.push({type: PAYLOAD_TYPE.INIT, p2: P2_UNUSED, buffer: serializedPathBuffer}); const messageBuffer = Buffer.from(message); @@ -33,7 +41,7 @@ function prepareBasicChunks(serializedPathBuffer, message) { if (i > buffer.length) { end = buffer.length; } - chunks.push({type: PAYLOAD_TYPE.ADD, buffer:buffer.slice(i, end)}); + chunks.push({type: PAYLOAD_TYPE.ADD, p2: P2_UNUSED, buffer:buffer.slice(i, end)}); } return chunks; @@ -49,6 +57,8 @@ function signGetChunksv1(path, options, getVersionResponse, message) { //ExtraInfo is either // - script hash from merkleIndex - initiates transaction signing with metadata // - "Sign message" - initiates message signing +// - "nft1" - initiates Setup NFT collection signing +// - "nft2" - initiates Transfer NFT signing // - anything else - initiates transaction sining without metadata function signGetChunksv2(path, options, getVersionResponse, message, extraInfo) { const serializedPath = serializePath(path, getVersionResponse, options); @@ -56,6 +66,19 @@ function signGetChunksv2(path, options, getVersionResponse, message, extraInfo) if (extraInfo == "Sign message") { basicChunks[basicChunks.length-1].type = PAYLOAD_TYPE.MESSAGE_LAST + basicChunks[basicChunks.length-1].p2 = P2_UNUSED + return basicChunks; + } + + if (extraInfo == "nft1") { + basicChunks[basicChunks.length-1].type = PAYLOAD_TYPE.LAST + basicChunks[basicChunks.length-1].p2 = PAYLOAD_TYPE_LAST_P2.NFT_SETUP_COLLECTION + return basicChunks; + } + + if (extraInfo == "nft2") { + basicChunks[basicChunks.length-1].type = PAYLOAD_TYPE.LAST + basicChunks[basicChunks.length-1].p2 = PAYLOAD_TYPE_LAST_P2.NFT_TRANSFER return basicChunks; } @@ -64,6 +87,7 @@ function signGetChunksv2(path, options, getVersionResponse, message, extraInfo) const merkleI = merkleIndex[scriptHash.slice(0, 16)] if (merkleI === undefined) { basicChunks[basicChunks.length-1].type = PAYLOAD_TYPE.LAST + basicChunks[basicChunks.length-1].p2 = PAYLOAD_TYPE_LAST_P2.ARBITRARY_MESSAGE_SIGNING return basicChunks; } // other chunks @@ -75,11 +99,11 @@ function signGetChunksv2(path, options, getVersionResponse, message, extraInfo) return [ ...basicChunks, - { type: PAYLOAD_TYPE.TX_METADATA, buffer: Buffer.from(metadata, "hex"), }, - { type: PAYLOAD_TYPE.MERKLE_TREE, buffer: Buffer.from(merkleTreeLevel1, "hex"), }, - { type: PAYLOAD_TYPE.MERKLE_TREE, buffer: Buffer.from(merkleTreeLevel2, "hex"), }, - { type: PAYLOAD_TYPE.MERKLE_TREE, buffer: Buffer.from(merkleTreeLevel3, "hex"), }, - { type: PAYLOAD_TYPE.MERKLE_TREE_LAST, buffer: Buffer.from(merkleTreeLevel4, "hex"), }, + { type: PAYLOAD_TYPE.TX_METADATA, p2: P2_UNUSED, buffer: Buffer.from(metadata, "hex"), }, + { type: PAYLOAD_TYPE.MERKLE_TREE, p2: P2_UNUSED, buffer: Buffer.from(merkleTreeLevel1, "hex"), }, + { type: PAYLOAD_TYPE.MERKLE_TREE, p2: P2_UNUSED, buffer: Buffer.from(merkleTreeLevel2, "hex"), }, + { type: PAYLOAD_TYPE.MERKLE_TREE, p2: P2_UNUSED, buffer: Buffer.from(merkleTreeLevel3, "hex"), }, + { type: PAYLOAD_TYPE.MERKLE_TREE_LAST, p2: P2_UNUSED, buffer: Buffer.from(merkleTreeLevel4, "hex"), }, ] } diff --git a/tests/script_parser.cpp b/tests/script_parser.cpp new file mode 100644 index 00000000..fc09f6a4 --- /dev/null +++ b/tests/script_parser.cpp @@ -0,0 +1,295 @@ +/******************************************************************************* +* (c) 2022 Vacuumlabs +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "gmock/gmock.h" +#include +#include +#include +#include +#include +#include +#include +#include + +testing::AssertionResult PARSE_TEST(std::string script, std::string template_, bool expectedResult, + std::vector expectedValues = {}) { + script_parsed_elements_t parsed; + bool result = parseScript(&parsed, (const uint8_t *) script.data(), script.size(), + (const uint8_t *) template_.data(), template_.size()); + if (result != expectedResult) { + return testing::AssertionFailure() << "Result: " << result << ", Expected: " << expectedResult; + } + if (result) { + if (parsed.elements_count != expectedValues.size()) { + return testing::AssertionFailure() << "Parsing error, Size: " << parsed.elements_count << ", Expected: " << expectedValues.size(); + } + + for(size_t i=0; i +testing::AssertionResult PARSE_NFT_TEST(NFTFunction parseNFTFunction, std::string script, bool expectedResult, + std::vector expectedValues = {}, + script_parsed_type_t expectedScriptType = SCRIPT_TYPE_UNKNOWN) { + script_parsed_elements_t parsed; + bool result = parseNFTFunction(&parsed, (const uint8_t *) script.data(), script.size()); + if (result != expectedResult) { + return testing::AssertionFailure() << "Result: " << result << ", Expected: " << expectedResult; + } + if (result) { + if (parsed.script_type != expectedScriptType) { + return testing::AssertionFailure() << "Parsing error, Script type: " << parsed.script_type << ", Expected: " << expectedScriptType; + } + + if (parsed.elements_count != expectedValues.size()) { + return testing::AssertionFailure() << "Parsing error, Size: " << parsed.elements_count << ", Expected: " << expectedValues.size(); + } + + for(size_t i=0; i(\n" + " /public/zzzzzzZ,\n" + " target: /storage/c\n" + " )\n" + " }\n" + "}\n"; + + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT1, script1, true, {"0x631e88ae7f1d7c20", "0x631e88ae7f1d7c20", + "aaa", "bbb", "c", "aaa", "c", "x", "_", "zzzzzzZ", "c"}, + SCRIPT_TYPE_NFT_SETUP_COLLECTION)); + + const char script2[] = + "import NonFungibleToken from 0x631e88ae7f1d7c20\n" + "import MetadataViews from 0x631e88ae7f1d7c20\n" + "import aaa from bbb\n" + "transaction {\n" + " prepare(acct: AuthAccount) {\n" + " let collectionType = acct.type(at: /storage/c)\n" + " // if there already is a collection stored, return\n" + " if (collectionType != nil) {\n" + " return\n" + " }\n" + " // create empty collection\n" + " let collection <- aaa.createEmptyCollection()\n" + " // put the new Collection in storage\n" + " acct.save(<-collection, to: /storage/cc)\n" + " // create a public capability for the collection\n" + " acct.link<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, x._, MetadataViews.ResolverCollection}>(\n" + " /public/zzzzzzZ,\n" + " target: /storage/c\n" + " )\n" + " }\n" + "}\n"; + + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT1, script2, false)); //storages do not match + + const char script3[] = + "import NonFungibleToken from 0x631e88ae7f1d7c20\n" + "import MetadataViews from 0x631e88ae7f1d7c20\n" + "import aaaa from bbb\n" + "transaction {\n" + " prepare(acct: AuthAccount) {\n" + " let collectionType = acct.type(at: /storage/c)\n" + " // if there already is a collection stored, return\n" + " if (collectionType != nil) {\n" + " return\n" + " }\n" + " // create empty collection\n" + " let collection <- aaa.createEmptyCollection()\n" + " // put the new Collection in storage\n" + " acct.save(<-collection, to: /storage/c)\n" + " // create a public capability for the collection\n" + " acct.link<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, x._, MetadataViews.ResolverCollection}>(\n" + " /public/zzzzzzZ,\n" + " target: /storage/c\n" + " )\n" + " }\n" + "}\n"; + + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT1, script3, false)); //contract names do not match +} + +TEST(script_parse, parseNFT2) { + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT2, "", false)); + + const char script1[] = + "import NonFungibleToken from 0x1d7e57aa55817448\n" + "import aaaa from bbb\n" + "transaction(recipient: Address, withdrawID: UInt64) {\n" + " // local variable for storing the transferred nft\n" + " let transferToken: @NonFungibleToken.NFT\n" + " prepare(owner: AuthAccount) {\n" + " // check if collection exists\n" + " if (owner.type(at: /storage/ststst) != Type<@aaaa.Collection>()) {\n" + " panic(\"Could not borrow a reference to the stored collection\")\n" + " }\n" + " // borrow a reference to the collection\n" + " let collectionRef = owner\n" + " .borrow<&aaaa.Collection>(from: /storage/ststst)!\n" + " // withdraw the NFT\n" + " self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID)\n" + " }\n" + " execute {\n" + " // get the recipient's public account object\n" + " let recipient = getAccount(recipient)\n" + " // get receivers capability\n" + " let nonFungibleTokenCapability = recipient\n" + " .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/publicPath)\n" + " // check the recipient has a NonFungibleToken public capability\n" + " if (!nonFungibleTokenCapability.check()) {\n" + " panic(\"Could not borrow a reference to the receiver's collection\")\n" + " }\n" + " // deposit nft to recipients collection\n" + " nonFungibleTokenCapability\n" + " .borrow()!\n" + " .deposit(token: <-self.transferToken)\n" + " }\n" + "}\n"; + + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT2, script1, true, {"0x1d7e57aa55817448", "aaaa", "bbb", "ststst", "aaaa", "aaaa", "ststst", "publicPath"}, + SCRIPT_TYPE_NFT_TRANSFER)); + + const char script2[] = + "import NonFungibleToken from 0x1d7e57aa55817448\n" + "import aaaa from bbb\n" + "transaction(recipient: Address, withdrawID: UInt64) {\n" + " // local variable for storing the transferred nft\n" + " let transferToken: @NonFungibleToken.NFT\n" + " prepare(owner: AuthAccount) {\n" + " // check if collection exists\n" + " if (owner.type(at: /storage/ststst) != Type<@aaaa.Collection>()) {\n" + " panic(\"Could not borrow a reference to the stored collection\")\n" + " }\n" + " // borrow a reference to the collection\n" + " let collectionRef = owner\n" + " .borrow<&aaaaa.Collection>(from: /storage/ststst)!\n" + " // withdraw the NFT\n" + " self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID)\n" + " }\n" + " execute {\n" + " // get the recipient's public account object\n" + " let recipient = getAccount(recipient)\n" + " // get receivers capability\n" + " let nonFungibleTokenCapability = recipient\n" + " .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/publicPath)\n" + " // check the recipient has a NonFungibleToken public capability\n" + " if (!nonFungibleTokenCapability.check()) {\n" + " panic(\"Could not borrow a reference to the receiver's collection\")\n" + " }\n" + " // deposit nft to recipients collection\n" + " nonFungibleTokenCapability\n" + " .borrow()!\n" + " .deposit(token: <-self.transferToken)\n" + " }\n" + "}\n"; + + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT2, script2, false)); //contractName mismatch + + const char script3[] = + "import NonFungibleToken from 0x1d7e57aa55817448\n" + "import aaaa from bbb\n" + "transaction(recipient: Address, withdrawID: UInt64) {\n" + " // local variable for storing the transferred nft\n" + " let transferToken: @NonFungibleToken.NFT\n" + " prepare(owner: AuthAccount) {\n" + " // check if collection exists\n" + " if (owner.type(at: /storage/stst) != Type<@aaaa.Collection>()) {\n" + " panic(\"Could not borrow a reference to the stored collection\")\n" + " }\n" + " // borrow a reference to the collection\n" + " let collectionRef = owner\n" + " .borrow<&aaaa.Collection>(from: /storage/ststst)!\n" + " // withdraw the NFT\n" + " self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID)\n" + " }\n" + " execute {\n" + " // get the recipient's public account object\n" + " let recipient = getAccount(recipient)\n" + " // get receivers capability\n" + " let nonFungibleTokenCapability = recipient\n" + " .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/publicPath)\n" + " // check the recipient has a NonFungibleToken public capability\n" + " if (!nonFungibleTokenCapability.check()) {\n" + " panic(\"Could not borrow a reference to the receiver's collection\")\n" + " }\n" + " // deposit nft to recipients collection\n" + " nonFungibleTokenCapability\n" + " .borrow()!\n" + " .deposit(token: <-self.transferToken)\n" + " }\n" + "}\n"; + + EXPECT_TRUE(PARSE_NFT_TEST(parseNFT2, script3, false)); // storage mismatch +} \ No newline at end of file diff --git a/tests/testvectors.cpp b/tests/testvectors.cpp index aab959ac..b002b781 100644 --- a/tests/testvectors.cpp +++ b/tests/testvectors.cpp @@ -51,7 +51,7 @@ void check_testcase(const testcase_t &testcase) { sha256((const uint8_t *) tc.script.c_str(), tc.script.length(), scriptHash); - err = parser_parse(&ctx, tc.blob.data(), tc.blob.size()); + err = parser_parse(&ctx, tc.blob.data(), tc.blob.size(), SCRIPT_TYPE_UNKNOWN); if (tc.valid) { ASSERT_EQ(err, PARSER_OK) << parser_getErrorDescription(err); } else { @@ -155,7 +155,6 @@ INSTANTIATE_TEST_SUITE_P( ::testing::ValuesIn(GetJsonTestCases("testvectors/manifestPayloadCases.json")), VerifyTestVectors::PrintToStringParamName() ); - TEST_P(VerifyTestVectors, CheckUIOutput_Manual) { check_testcase(GetParam()); } #pragma clang diagnostic pop diff --git a/tests/tx_metadata.cpp b/tests/tx_metadata.cpp index 128dc247..a0a31c3d 100644 --- a/tests/tx_metadata.cpp +++ b/tests/tx_metadata.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2020 Zondax GmbH +* (c) 2022 Vacuumlabs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,7 @@ TEST(tx_data, validateScriptHash) { } -TEST(tx_data, parseCompressedTxData) { +TEST(tx_metadata, parseCompressedTxData) { parser_error_t err; parsed_tx_metadata_t result; err = _parseTxMetadata(hashTokenTranfer1, TX_METADATA_ADD_NEW_KEY, sizeof(TX_METADATA_ADD_NEW_KEY), &result); diff --git a/tests_speculos/package.json b/tests_speculos/package.json index 1f3bc6d5..d54f2444 100644 --- a/tests_speculos/package.json +++ b/tests_speculos/package.json @@ -8,6 +8,8 @@ "dependencies": { "@ledgerhq/hw-transport-node-speculos": "^6.20.0", "@onflow/ledger": "file:../js", + "@onflow/encode": "^0.0.8", + "deepmerge": "^4.2.2", "assert": "^2.0.0", "strict": "^0.0.0" }, diff --git a/tests_speculos/test-transaction-nft.js b/tests_speculos/test-transaction-nft.js new file mode 100644 index 00000000..4e043439 --- /dev/null +++ b/tests_speculos/test-transaction-nft.js @@ -0,0 +1,429 @@ +'use strict'; + +import merge from "deepmerge"; +import { testStart, testEnd, testStep, compareInAPDU, compareOutAPDU, noMoreAPDUs, compareGetVersionAPDUs, getScriptName, getSpeculosDefaultConf, humanTime } from "./speculos-common.js"; +import { getSpyTransport } from "./speculos-transport.js"; +import { getButtonsAndSnapshots } from "./speculos-buttons-and-snapshots.js"; +import { default as OnflowLedgerMod } from "@onflow/ledger"; +import { fileURLToPath, pathToFileURL } from 'url'; +import assert from 'assert/strict'; +import { encodeTransactionPayload, encodeTransactionEnvelope } from "@onflow/encode"; +import pkg from 'elliptic'; +const {ec: EC} = pkg; +import jsSHA from "jssha"; + +const scriptName = getScriptName(fileURLToPath(import.meta.url)); +testStart(scriptName); + +const speculosConf = getSpeculosDefaultConf(); +const transport = await getSpyTransport(speculosConf); +const FlowApp = OnflowLedgerMod.default; +const app = new FlowApp(transport); +const device = getButtonsAndSnapshots(scriptName, speculosConf); + +const ECDSA_SECP256K1 = { name: "secp256k1", code: FlowApp.Signature.SECP256K1, pathCode: 0x200 }; +const ECDSA_P256 = { name: "p256", code: FlowApp.Signature.P256, pathCode: 0x300}; + +const SHA2_256 = { name: "SHA-256", code: FlowApp.Hash.SHA2_256, pathCode: 0x01}; +const SHA3_256 = { name: "SHA3-256", code: FlowApp.Hash.SHA3_256, pathCode: 0x03}; + +const path = `m/44'/539'/0'/0/0`; +const options = ECDSA_P256.code | SHA3_256.code + +const EMULATOR = "Emulator"; +const TESTNET = "Testnet"; +const MAINNET = "Mainnet"; + +const ADDRESS_EMULATOR = "ed2d4f9eb8bcd4ac"; +const ADDRESS_TESTNET = "99a8ac2c71d4f6bd"; +const ADDRESS_MAINNET = "f19c161bc24cf4b4"; + +const ADDRESSES = { + [EMULATOR]: ADDRESS_EMULATOR, + [TESTNET]: ADDRESS_TESTNET, + [MAINNET]: ADDRESS_MAINNET, +}; + +const basePayloadTx = (network) => { + const address = ADDRESSES[network]; + + return { + script: "", + arguments: [], + refBlock: "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b", + gasLimit: 42, + proposalKey: { + address: address, + keyId: 4, + sequenceNum: 10, + }, + payer: address, + authorizers: [address], + }; +}; + +const combineMerge = (target, source, options) => { + // empty list always overwrites target + if (source.length == 0) return source + + const destination = target.slice() + + source.forEach((item, index) => { + if (typeof destination[index] === "undefined") { + destination[index] = options.cloneUnlessOtherwiseSpecified(item, options) + } else if (options.isMergeableObject(item)) { + destination[index] = merge(target[index], item, options) + } else if (target.indexOf(item) === -1) { + destination.push(item) + } + }) + + return destination +}; + +const buildPayloadTx = (network, partialTx) => + merge(basePayloadTx(network), partialTx, {arrayMerge: combineMerge}); + + +const getTxEnvelope = (network, script, args) => { + const tx = buildPayloadTx(network, { + script: script, + arguments: args, + }) + return encodeTransactionEnvelope({...tx, payloadSigs: []}); +} + +//--------------------------TESTS----------------------------- + +await device.makeStartingScreenshot(); + +// We get pubkey so we can verify signature +testStep(" - - -", "await app.getAddressAndPubKey() // path=" + path); +const getPubkeyResponse = await app.getAddressAndPubKey(path, options); +assert.equal(getPubkeyResponse.returnCode, 0x9000); +assert.equal(getPubkeyResponse.errorMessage, "No errors"); +const pubkeyHex = getPubkeyResponse.publicKey.toString("hex") + + +{ + const contractName = "contractName" + const contractAddress = "contractAddress" + const storagePath = "storagePath" + const publicCollectionContractName = "publicCollectionContractName" + const publicCollectionName = "publicCollectionName" + const publicPath = "publicPath" + const script = +`import NonFungibleToken from 0x1d7e57aa55817448 +import MetadataViews from 0x1d7e57aa55817448 +import ${contractName} from ${contractAddress} +transaction { + prepare(acct: AuthAccount) { + let collectionType = acct.type(at: /storage/${storagePath}) + // if there already is a collection stored, return + if (collectionType != nil) { + return + } + // create empty collection + let collection <- ${contractName}.createEmptyCollection() + // put the new Collection in storage + acct.save(<-collection, to: /storage/${storagePath}) + // create a public capability for the collection + acct.link<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, ${publicCollectionContractName}.${publicCollectionName}, MetadataViews.ResolverCollection}>( + /public/${publicPath}, + target: /storage/${storagePath} + ) + } +} +` + const args = [] + const txBlob = Buffer.from(getTxEnvelope(MAINNET, script, args), "hex") + console.log("Script") + console.log(script) + console.log("Tx built") + console.log(txBlob.toString("hex")) + + testStep(" - - -", "NFT 1 mainnet correct"); + const signPromise = app.sign(path, txBlob, options, "nft1"); + await device.review("Review transaction"); + const signResponse = await signPromise; + console.log(signResponse) + assert.equal(signResponse.returnCode, 0x9000); + assert.equal(signResponse.errorMessage, "No errors"); + + let tag = Buffer.alloc(32); + tag.write("FLOW-V0.0-transaction"); + const hasher = new jsSHA(SHA3_256.name, "UINT8ARRAY"); + hasher.update(tag); + hasher.update(txBlob); + const digestHex = hasher.getHash("HEX"); + const ec = new EC(ECDSA_P256.name); + assert.ok(ec.verify(digestHex, signResponse.signatureDER.toString("hex"), pubkeyHex, 'hex')); +} + +{ + const contractName = "contractName" + const contractAddress = "contractAddress" + const storagePath = "storagePath" + const publicPath = "publicPath" + const script = +`import NonFungibleToken from 0x1d7e57aa55817448 +import ${contractName} from ${contractAddress} +transaction(recipient: Address, withdrawID: UInt64) { + // local variable for storing the transferred nft + let transferToken: @NonFungibleToken.NFT + prepare(owner: AuthAccount) { + // check if collection exists + if (owner.type(at: /storage/${storagePath}) != Type<@${contractName}.Collection>()) { + panic("Could not borrow a reference to the stored collection") + } + // borrow a reference to the collection + let collectionRef = owner + .borrow<&${contractName}.Collection>(from: /storage/${storagePath})! + // withdraw the NFT + self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID) + } + execute { + // get the recipient's public account object + let recipient = getAccount(recipient) + // get receivers capability + let nonFungibleTokenCapability = recipient + .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/${publicPath}) + // check the recipient has a NonFungibleToken public capability + if (!nonFungibleTokenCapability.check()) { + panic("Could not borrow a reference to the receiver's collection") + } + // deposit nft to recipients collection + nonFungibleTokenCapability + .borrow()! + .deposit(token: <-self.transferToken) + } +} +` + const args = [ + { + "type": "Address", + "value": "Ny nice address" + }, + { + "type": "UInt64", + "value": "123456" + }, + ] + const txBlob = Buffer.from(getTxEnvelope(MAINNET, script, args), "hex") + console.log("Script") + console.log(script) + console.log("Tx built") + console.log(txBlob.toString("hex")) + + testStep(" - - -", "NFT 2 mainnet correct"); + const signPromise = app.sign(path, txBlob, options, "nft2"); + await device.review("Review transaction"); + const signResponse = await signPromise; + console.log(signResponse) + assert.equal(signResponse.returnCode, 0x9000); + assert.equal(signResponse.errorMessage, "No errors"); + + let tag = Buffer.alloc(32); + tag.write("FLOW-V0.0-transaction"); + const hasher = new jsSHA(SHA3_256.name, "UINT8ARRAY"); + hasher.update(tag); + hasher.update(txBlob); + const digestHex = hasher.getHash("HEX"); + const ec = new EC(ECDSA_P256.name); + assert.ok(ec.verify(digestHex, signResponse.signatureDER.toString("hex"), pubkeyHex, 'hex')); +} + +{ + const contractName = "contractName" + const contractAddress = "contractAddress" + const storagePath = "storagePath" + const publicCollectionContractName = "publicCollectionContractName" + const publicCollectionName = "publicCollectionName" + const publicPath = "publicPath" + const script = +`import NonFungibleToken from 0x631e88ae7f1d7c20 +import MetadataViews from 0x631e88ae7f1d7c20 +import ${contractName} from ${contractAddress} +transaction { + prepare(acct: AuthAccount) { + let collectionType = acct.type(at: /storage/${storagePath}) + // if there already is a collection stored, return + if (collectionType != nil) { + return + } + // create empty collection + let collection <- ${contractName}.createEmptyCollection() + // put the new Collection in storage + acct.save(<-collection, to: /storage/${storagePath}) + // create a public capability for the collection + acct.link<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, ${publicCollectionContractName}.${publicCollectionName}, MetadataViews.ResolverCollection}>( + /public/${publicPath}, + target: /storage/${storagePath} + ) + } +} +` + const args = [] + const txBlob = Buffer.from(getTxEnvelope(TESTNET, script, args), "hex") + console.log("Script") + console.log(script) + console.log("Tx built") + console.log(txBlob.toString("hex")) + + testStep(" - - -", "NFT 1 testnet correct"); + const signPromise = app.sign(path, txBlob, options, "nft1"); + await device.review("Review transaction"); + const signResponse = await signPromise; + console.log(signResponse) + assert.equal(signResponse.returnCode, 0x9000); + assert.equal(signResponse.errorMessage, "No errors"); + + let tag = Buffer.alloc(32); + tag.write("FLOW-V0.0-transaction"); + const hasher = new jsSHA(SHA3_256.name, "UINT8ARRAY"); + hasher.update(tag); + hasher.update(txBlob); + const digestHex = hasher.getHash("HEX"); + const ec = new EC(ECDSA_P256.name); + assert.ok(ec.verify(digestHex, signResponse.signatureDER.toString("hex"), pubkeyHex, 'hex')); +} + +{ + const contractName = "contractName" + const contractAddress = "contractAddress" + const storagePath = "storagePath" + const publicPath = "publicPath" + const script = +`import NonFungibleToken from 0x631e88ae7f1d7c20 +import ${contractName} from ${contractAddress} +transaction(recipient: Address, withdrawID: UInt64) { + // local variable for storing the transferred nft + let transferToken: @NonFungibleToken.NFT + prepare(owner: AuthAccount) { + // check if collection exists + if (owner.type(at: /storage/${storagePath}) != Type<@${contractName}.Collection>()) { + panic("Could not borrow a reference to the stored collection") + } + // borrow a reference to the collection + let collectionRef = owner + .borrow<&${contractName}.Collection>(from: /storage/${storagePath})! + // withdraw the NFT + self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID) + } + execute { + // get the recipient's public account object + let recipient = getAccount(recipient) + // get receivers capability + let nonFungibleTokenCapability = recipient + .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/${publicPath}) + // check the recipient has a NonFungibleToken public capability + if (!nonFungibleTokenCapability.check()) { + panic("Could not borrow a reference to the receiver's collection") + } + // deposit nft to recipients collection + nonFungibleTokenCapability + .borrow()! + .deposit(token: <-self.transferToken) + } +} +` + const args = [ + { + "type": "Address", + "value": "Ny nice address" + }, + { + "type": "UInt64", + "value": "123456" + }, + ] + const txBlob = Buffer.from(getTxEnvelope(MAINNET, script, args), "hex") + console.log("Script") + console.log(script) + console.log("Tx built") + console.log(txBlob.toString("hex")) + + testStep(" - - -", "NFT 2 testnet correct"); + const signPromise = app.sign(path, txBlob, options, "nft2"); + await device.review("Review transaction"); + const signResponse = await signPromise; + console.log(signResponse) + assert.equal(signResponse.returnCode, 0x9000); + assert.equal(signResponse.errorMessage, "No errors"); + + let tag = Buffer.alloc(32); + tag.write("FLOW-V0.0-transaction"); + const hasher = new jsSHA(SHA3_256.name, "UINT8ARRAY"); + hasher.update(tag); + hasher.update(txBlob); + const digestHex = hasher.getHash("HEX"); + const ec = new EC(ECDSA_P256.name); + assert.ok(ec.verify(digestHex, signResponse.signatureDER.toString("hex"), pubkeyHex, 'hex')); +} + +{ + const contractName = "contractName" + const contractAddress = "contractAddress" + const storagePath = "storagePath" + const publicPath = "publicPath" + const script = +`import NonFungibleToken from 0x631e88ae7f1d7c21 +import ${contractName} from ${contractAddress} +transaction(recipient: Address, withdrawID: UInt64) { + // local variable for storing the transferred nft + let transferToken: @NonFungibleToken.NFT + prepare(owner: AuthAccount) { + // check if collection exists + if (owner.type(at: /storage/${storagePath}) != Type<@${contractName}.Collection>()) { + panic("Could not borrow a reference to the stored collection") + } + // borrow a reference to the collection + let collectionRef = owner + .borrow<&${contractName}.Collection>(from: /storage/${storagePath})! + // withdraw the NFT + self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID) + } + execute { + // get the recipient's public account object + let recipient = getAccount(recipient) + // get receivers capability + let nonFungibleTokenCapability = recipient + .getCapability<&{NonFungibleToken.CollectionPublic}>(/public/${publicPath}) + // check the recipient has a NonFungibleToken public capability + if (!nonFungibleTokenCapability.check()) { + panic("Could not borrow a reference to the receiver's collection") + } + // deposit nft to recipients collection + nonFungibleTokenCapability + .borrow()! + .deposit(token: <-self.transferToken) + } +} +` + const args = [ + { + "type": "Address", + "value": "Ny nice address" + }, + { + "type": "UInt64", + "value": "123456" + }, + ] + const txBlob = Buffer.from(getTxEnvelope(MAINNET, script, args), "hex") + console.log("Script") + console.log(script) + console.log("Tx with invalid contract address build") + console.log(txBlob.toString("hex")) + + testStep(" - - -", "NFT 2 testnetinvalid address"); + const signPromise = app.sign(path, txBlob, options, "nft2"); + const signResponse = await signPromise; + assert.equal(signResponse.returnCode, 0x6984); + assert.equal(signResponse.errorMessage, "Data is invalid : Unexpected script"); +} + + +await transport.close() +testEnd(scriptName); +process.stdin.pause() diff --git a/tests_speculos/test-transaction-nft/nanos.01.png b/tests_speculos/test-transaction-nft/nanos.01.png new file mode 100644 index 00000000..7f6ab5e6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.01.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.02.png b/tests_speculos/test-transaction-nft/nanos.02.png new file mode 100644 index 00000000..984a8179 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.02.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.03.png b/tests_speculos/test-transaction-nft/nanos.03.png new file mode 100644 index 00000000..1eba76f2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.03.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.04.png b/tests_speculos/test-transaction-nft/nanos.04.png new file mode 100644 index 00000000..a5231df2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.04.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.05.png b/tests_speculos/test-transaction-nft/nanos.05.png new file mode 100644 index 00000000..a5e4b01b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.05.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.06.png b/tests_speculos/test-transaction-nft/nanos.06.png new file mode 100644 index 00000000..b5b3233f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.06.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.07.png b/tests_speculos/test-transaction-nft/nanos.07.png new file mode 100644 index 00000000..daf20c86 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.07.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.08.png b/tests_speculos/test-transaction-nft/nanos.08.png new file mode 100644 index 00000000..0da4c205 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.08.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.09.png b/tests_speculos/test-transaction-nft/nanos.09.png new file mode 100644 index 00000000..2ab14840 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.09.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.10.png b/tests_speculos/test-transaction-nft/nanos.10.png new file mode 100644 index 00000000..05f27c3a Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.10.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.11.png b/tests_speculos/test-transaction-nft/nanos.11.png new file mode 100644 index 00000000..5ebc5665 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.11.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.12.png b/tests_speculos/test-transaction-nft/nanos.12.png new file mode 100644 index 00000000..f39812e8 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.12.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.13.png b/tests_speculos/test-transaction-nft/nanos.13.png new file mode 100644 index 00000000..41b0ce2b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.13.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.14.png b/tests_speculos/test-transaction-nft/nanos.14.png new file mode 100644 index 00000000..44e7069e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.14.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.15.png b/tests_speculos/test-transaction-nft/nanos.15.png new file mode 100644 index 00000000..7a5610fa Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.15.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.16.png b/tests_speculos/test-transaction-nft/nanos.16.png new file mode 100644 index 00000000..55ce7183 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.16.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.17.png b/tests_speculos/test-transaction-nft/nanos.17.png new file mode 100644 index 00000000..c4016375 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.17.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.18.png b/tests_speculos/test-transaction-nft/nanos.18.png new file mode 100644 index 00000000..52b06db9 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.18.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.19.png b/tests_speculos/test-transaction-nft/nanos.19.png new file mode 100644 index 00000000..006c26ab Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.19.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.20.png b/tests_speculos/test-transaction-nft/nanos.20.png new file mode 100644 index 00000000..7f6ab5e6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.20.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.21.png b/tests_speculos/test-transaction-nft/nanos.21.png new file mode 100644 index 00000000..e7b4c952 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.21.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.22.png b/tests_speculos/test-transaction-nft/nanos.22.png new file mode 100644 index 00000000..1eba76f2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.22.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.23.png b/tests_speculos/test-transaction-nft/nanos.23.png new file mode 100644 index 00000000..a5231df2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.23.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.24.png b/tests_speculos/test-transaction-nft/nanos.24.png new file mode 100644 index 00000000..a5e4b01b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.24.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.25.png b/tests_speculos/test-transaction-nft/nanos.25.png new file mode 100644 index 00000000..0da4c205 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.25.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.26.png b/tests_speculos/test-transaction-nft/nanos.26.png new file mode 100644 index 00000000..2ab14840 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.26.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.27.png b/tests_speculos/test-transaction-nft/nanos.27.png new file mode 100644 index 00000000..63da803f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.27.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.28.png b/tests_speculos/test-transaction-nft/nanos.28.png new file mode 100644 index 00000000..5064ec2b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.28.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.29.png b/tests_speculos/test-transaction-nft/nanos.29.png new file mode 100644 index 00000000..05f27c3a Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.29.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.30.png b/tests_speculos/test-transaction-nft/nanos.30.png new file mode 100644 index 00000000..5ebc5665 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.30.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.31.png b/tests_speculos/test-transaction-nft/nanos.31.png new file mode 100644 index 00000000..f39812e8 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.31.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.32.png b/tests_speculos/test-transaction-nft/nanos.32.png new file mode 100644 index 00000000..41b0ce2b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.32.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.33.png b/tests_speculos/test-transaction-nft/nanos.33.png new file mode 100644 index 00000000..44e7069e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.33.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.34.png b/tests_speculos/test-transaction-nft/nanos.34.png new file mode 100644 index 00000000..7a5610fa Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.34.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.35.png b/tests_speculos/test-transaction-nft/nanos.35.png new file mode 100644 index 00000000..55ce7183 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.35.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.36.png b/tests_speculos/test-transaction-nft/nanos.36.png new file mode 100644 index 00000000..c4016375 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.36.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.37.png b/tests_speculos/test-transaction-nft/nanos.37.png new file mode 100644 index 00000000..52b06db9 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.37.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.38.png b/tests_speculos/test-transaction-nft/nanos.38.png new file mode 100644 index 00000000..006c26ab Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.38.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.39.png b/tests_speculos/test-transaction-nft/nanos.39.png new file mode 100644 index 00000000..7f6ab5e6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.39.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.40.png b/tests_speculos/test-transaction-nft/nanos.40.png new file mode 100644 index 00000000..984a8179 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.40.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.41.png b/tests_speculos/test-transaction-nft/nanos.41.png new file mode 100644 index 00000000..1eba76f2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.41.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.42.png b/tests_speculos/test-transaction-nft/nanos.42.png new file mode 100644 index 00000000..a5231df2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.42.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.43.png b/tests_speculos/test-transaction-nft/nanos.43.png new file mode 100644 index 00000000..a5e4b01b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.43.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.44.png b/tests_speculos/test-transaction-nft/nanos.44.png new file mode 100644 index 00000000..b5b3233f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.44.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.45.png b/tests_speculos/test-transaction-nft/nanos.45.png new file mode 100644 index 00000000..daf20c86 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.45.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.46.png b/tests_speculos/test-transaction-nft/nanos.46.png new file mode 100644 index 00000000..0da4c205 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.46.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.47.png b/tests_speculos/test-transaction-nft/nanos.47.png new file mode 100644 index 00000000..f2422205 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.47.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.48.png b/tests_speculos/test-transaction-nft/nanos.48.png new file mode 100644 index 00000000..05f27c3a Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.48.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.49.png b/tests_speculos/test-transaction-nft/nanos.49.png new file mode 100644 index 00000000..5ebc5665 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.49.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.50.png b/tests_speculos/test-transaction-nft/nanos.50.png new file mode 100644 index 00000000..f39812e8 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.50.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.51.png b/tests_speculos/test-transaction-nft/nanos.51.png new file mode 100644 index 00000000..7a7c2869 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.51.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.52.png b/tests_speculos/test-transaction-nft/nanos.52.png new file mode 100644 index 00000000..44e7069e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.52.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.53.png b/tests_speculos/test-transaction-nft/nanos.53.png new file mode 100644 index 00000000..7a5610fa Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.53.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.54.png b/tests_speculos/test-transaction-nft/nanos.54.png new file mode 100644 index 00000000..61e19bd5 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.54.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.55.png b/tests_speculos/test-transaction-nft/nanos.55.png new file mode 100644 index 00000000..0a500512 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.55.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.56.png b/tests_speculos/test-transaction-nft/nanos.56.png new file mode 100644 index 00000000..52b06db9 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.56.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.57.png b/tests_speculos/test-transaction-nft/nanos.57.png new file mode 100644 index 00000000..006c26ab Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.57.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.58.png b/tests_speculos/test-transaction-nft/nanos.58.png new file mode 100644 index 00000000..7f6ab5e6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.58.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.59.png b/tests_speculos/test-transaction-nft/nanos.59.png new file mode 100644 index 00000000..e7b4c952 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.59.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.60.png b/tests_speculos/test-transaction-nft/nanos.60.png new file mode 100644 index 00000000..1eba76f2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.60.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.61.png b/tests_speculos/test-transaction-nft/nanos.61.png new file mode 100644 index 00000000..a5231df2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.61.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.62.png b/tests_speculos/test-transaction-nft/nanos.62.png new file mode 100644 index 00000000..a5e4b01b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.62.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.63.png b/tests_speculos/test-transaction-nft/nanos.63.png new file mode 100644 index 00000000..0da4c205 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.63.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.64.png b/tests_speculos/test-transaction-nft/nanos.64.png new file mode 100644 index 00000000..2ab14840 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.64.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.65.png b/tests_speculos/test-transaction-nft/nanos.65.png new file mode 100644 index 00000000..63da803f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.65.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.66.png b/tests_speculos/test-transaction-nft/nanos.66.png new file mode 100644 index 00000000..5064ec2b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.66.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.67.png b/tests_speculos/test-transaction-nft/nanos.67.png new file mode 100644 index 00000000..05f27c3a Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.67.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.68.png b/tests_speculos/test-transaction-nft/nanos.68.png new file mode 100644 index 00000000..5ebc5665 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.68.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.69.png b/tests_speculos/test-transaction-nft/nanos.69.png new file mode 100644 index 00000000..f39812e8 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.69.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.70.png b/tests_speculos/test-transaction-nft/nanos.70.png new file mode 100644 index 00000000..41b0ce2b Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.70.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.71.png b/tests_speculos/test-transaction-nft/nanos.71.png new file mode 100644 index 00000000..44e7069e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.71.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.72.png b/tests_speculos/test-transaction-nft/nanos.72.png new file mode 100644 index 00000000..7a5610fa Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.72.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.73.png b/tests_speculos/test-transaction-nft/nanos.73.png new file mode 100644 index 00000000..55ce7183 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.73.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.74.png b/tests_speculos/test-transaction-nft/nanos.74.png new file mode 100644 index 00000000..c4016375 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.74.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.75.png b/tests_speculos/test-transaction-nft/nanos.75.png new file mode 100644 index 00000000..52b06db9 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.75.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.76.png b/tests_speculos/test-transaction-nft/nanos.76.png new file mode 100644 index 00000000..006c26ab Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.76.png differ diff --git a/tests_speculos/test-transaction-nft/nanos.77.png b/tests_speculos/test-transaction-nft/nanos.77.png new file mode 100644 index 00000000..7f6ab5e6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanos.77.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.01.png b/tests_speculos/test-transaction-nft/nanox.01.png new file mode 100644 index 00000000..dbbdb276 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.01.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.02.png b/tests_speculos/test-transaction-nft/nanox.02.png new file mode 100644 index 00000000..2689ddb2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.02.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.03.png b/tests_speculos/test-transaction-nft/nanox.03.png new file mode 100644 index 00000000..10c31bc9 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.03.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.04.png b/tests_speculos/test-transaction-nft/nanox.04.png new file mode 100644 index 00000000..a5eebf2f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.04.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.05.png b/tests_speculos/test-transaction-nft/nanox.05.png new file mode 100644 index 00000000..4d021cac Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.05.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.06.png b/tests_speculos/test-transaction-nft/nanox.06.png new file mode 100644 index 00000000..658aa67d Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.06.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.07.png b/tests_speculos/test-transaction-nft/nanox.07.png new file mode 100644 index 00000000..37e79044 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.07.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.08.png b/tests_speculos/test-transaction-nft/nanox.08.png new file mode 100644 index 00000000..7e9a53ce Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.08.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.09.png b/tests_speculos/test-transaction-nft/nanox.09.png new file mode 100644 index 00000000..5481d5da Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.09.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.10.png b/tests_speculos/test-transaction-nft/nanox.10.png new file mode 100644 index 00000000..d1eb962c Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.10.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.11.png b/tests_speculos/test-transaction-nft/nanox.11.png new file mode 100644 index 00000000..11ac632e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.11.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.12.png b/tests_speculos/test-transaction-nft/nanox.12.png new file mode 100644 index 00000000..62827380 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.12.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.13.png b/tests_speculos/test-transaction-nft/nanox.13.png new file mode 100644 index 00000000..3ad758de Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.13.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.14.png b/tests_speculos/test-transaction-nft/nanox.14.png new file mode 100644 index 00000000..bff4ff72 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.14.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.15.png b/tests_speculos/test-transaction-nft/nanox.15.png new file mode 100644 index 00000000..c02f5ccb Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.15.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.16.png b/tests_speculos/test-transaction-nft/nanox.16.png new file mode 100644 index 00000000..510dbb77 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.16.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.17.png b/tests_speculos/test-transaction-nft/nanox.17.png new file mode 100644 index 00000000..8099d16e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.17.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.18.png b/tests_speculos/test-transaction-nft/nanox.18.png new file mode 100644 index 00000000..cc0bd7b2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.18.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.19.png b/tests_speculos/test-transaction-nft/nanox.19.png new file mode 100644 index 00000000..1e4be699 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.19.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.20.png b/tests_speculos/test-transaction-nft/nanox.20.png new file mode 100644 index 00000000..dbbdb276 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.20.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.21.png b/tests_speculos/test-transaction-nft/nanox.21.png new file mode 100644 index 00000000..2689ddb2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.21.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.22.png b/tests_speculos/test-transaction-nft/nanox.22.png new file mode 100644 index 00000000..de1bd423 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.22.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.23.png b/tests_speculos/test-transaction-nft/nanox.23.png new file mode 100644 index 00000000..a5eebf2f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.23.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.24.png b/tests_speculos/test-transaction-nft/nanox.24.png new file mode 100644 index 00000000..4d021cac Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.24.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.25.png b/tests_speculos/test-transaction-nft/nanox.25.png new file mode 100644 index 00000000..658aa67d Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.25.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.26.png b/tests_speculos/test-transaction-nft/nanox.26.png new file mode 100644 index 00000000..7e9a53ce Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.26.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.27.png b/tests_speculos/test-transaction-nft/nanox.27.png new file mode 100644 index 00000000..5481d5da Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.27.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.28.png b/tests_speculos/test-transaction-nft/nanox.28.png new file mode 100644 index 00000000..ff4e54c6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.28.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.29.png b/tests_speculos/test-transaction-nft/nanox.29.png new file mode 100644 index 00000000..0a3048fd Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.29.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.30.png b/tests_speculos/test-transaction-nft/nanox.30.png new file mode 100644 index 00000000..d1eb962c Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.30.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.31.png b/tests_speculos/test-transaction-nft/nanox.31.png new file mode 100644 index 00000000..11ac632e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.31.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.32.png b/tests_speculos/test-transaction-nft/nanox.32.png new file mode 100644 index 00000000..62827380 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.32.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.33.png b/tests_speculos/test-transaction-nft/nanox.33.png new file mode 100644 index 00000000..3ad758de Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.33.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.34.png b/tests_speculos/test-transaction-nft/nanox.34.png new file mode 100644 index 00000000..bff4ff72 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.34.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.35.png b/tests_speculos/test-transaction-nft/nanox.35.png new file mode 100644 index 00000000..c02f5ccb Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.35.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.36.png b/tests_speculos/test-transaction-nft/nanox.36.png new file mode 100644 index 00000000..510dbb77 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.36.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.37.png b/tests_speculos/test-transaction-nft/nanox.37.png new file mode 100644 index 00000000..8099d16e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.37.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.38.png b/tests_speculos/test-transaction-nft/nanox.38.png new file mode 100644 index 00000000..cc0bd7b2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.38.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.39.png b/tests_speculos/test-transaction-nft/nanox.39.png new file mode 100644 index 00000000..1e4be699 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.39.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.40.png b/tests_speculos/test-transaction-nft/nanox.40.png new file mode 100644 index 00000000..dbbdb276 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.40.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.41.png b/tests_speculos/test-transaction-nft/nanox.41.png new file mode 100644 index 00000000..2689ddb2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.41.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.42.png b/tests_speculos/test-transaction-nft/nanox.42.png new file mode 100644 index 00000000..10c31bc9 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.42.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.43.png b/tests_speculos/test-transaction-nft/nanox.43.png new file mode 100644 index 00000000..a5eebf2f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.43.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.44.png b/tests_speculos/test-transaction-nft/nanox.44.png new file mode 100644 index 00000000..4d021cac Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.44.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.45.png b/tests_speculos/test-transaction-nft/nanox.45.png new file mode 100644 index 00000000..658aa67d Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.45.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.46.png b/tests_speculos/test-transaction-nft/nanox.46.png new file mode 100644 index 00000000..37e79044 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.46.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.47.png b/tests_speculos/test-transaction-nft/nanox.47.png new file mode 100644 index 00000000..7e9a53ce Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.47.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.48.png b/tests_speculos/test-transaction-nft/nanox.48.png new file mode 100644 index 00000000..9badf1a4 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.48.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.49.png b/tests_speculos/test-transaction-nft/nanox.49.png new file mode 100644 index 00000000..d1eb962c Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.49.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.50.png b/tests_speculos/test-transaction-nft/nanox.50.png new file mode 100644 index 00000000..11ac632e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.50.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.51.png b/tests_speculos/test-transaction-nft/nanox.51.png new file mode 100644 index 00000000..62827380 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.51.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.52.png b/tests_speculos/test-transaction-nft/nanox.52.png new file mode 100644 index 00000000..70d85793 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.52.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.53.png b/tests_speculos/test-transaction-nft/nanox.53.png new file mode 100644 index 00000000..bff4ff72 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.53.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.54.png b/tests_speculos/test-transaction-nft/nanox.54.png new file mode 100644 index 00000000..c02f5ccb Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.54.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.55.png b/tests_speculos/test-transaction-nft/nanox.55.png new file mode 100644 index 00000000..3df973c5 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.55.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.56.png b/tests_speculos/test-transaction-nft/nanox.56.png new file mode 100644 index 00000000..bcbf619c Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.56.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.57.png b/tests_speculos/test-transaction-nft/nanox.57.png new file mode 100644 index 00000000..cc0bd7b2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.57.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.58.png b/tests_speculos/test-transaction-nft/nanox.58.png new file mode 100644 index 00000000..1e4be699 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.58.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.59.png b/tests_speculos/test-transaction-nft/nanox.59.png new file mode 100644 index 00000000..dbbdb276 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.59.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.60.png b/tests_speculos/test-transaction-nft/nanox.60.png new file mode 100644 index 00000000..2689ddb2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.60.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.61.png b/tests_speculos/test-transaction-nft/nanox.61.png new file mode 100644 index 00000000..de1bd423 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.61.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.62.png b/tests_speculos/test-transaction-nft/nanox.62.png new file mode 100644 index 00000000..a5eebf2f Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.62.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.63.png b/tests_speculos/test-transaction-nft/nanox.63.png new file mode 100644 index 00000000..4d021cac Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.63.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.64.png b/tests_speculos/test-transaction-nft/nanox.64.png new file mode 100644 index 00000000..658aa67d Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.64.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.65.png b/tests_speculos/test-transaction-nft/nanox.65.png new file mode 100644 index 00000000..7e9a53ce Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.65.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.66.png b/tests_speculos/test-transaction-nft/nanox.66.png new file mode 100644 index 00000000..5481d5da Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.66.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.67.png b/tests_speculos/test-transaction-nft/nanox.67.png new file mode 100644 index 00000000..ff4e54c6 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.67.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.68.png b/tests_speculos/test-transaction-nft/nanox.68.png new file mode 100644 index 00000000..0a3048fd Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.68.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.69.png b/tests_speculos/test-transaction-nft/nanox.69.png new file mode 100644 index 00000000..d1eb962c Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.69.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.70.png b/tests_speculos/test-transaction-nft/nanox.70.png new file mode 100644 index 00000000..11ac632e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.70.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.71.png b/tests_speculos/test-transaction-nft/nanox.71.png new file mode 100644 index 00000000..62827380 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.71.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.72.png b/tests_speculos/test-transaction-nft/nanox.72.png new file mode 100644 index 00000000..3ad758de Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.72.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.73.png b/tests_speculos/test-transaction-nft/nanox.73.png new file mode 100644 index 00000000..bff4ff72 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.73.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.74.png b/tests_speculos/test-transaction-nft/nanox.74.png new file mode 100644 index 00000000..c02f5ccb Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.74.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.75.png b/tests_speculos/test-transaction-nft/nanox.75.png new file mode 100644 index 00000000..510dbb77 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.75.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.76.png b/tests_speculos/test-transaction-nft/nanox.76.png new file mode 100644 index 00000000..8099d16e Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.76.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.77.png b/tests_speculos/test-transaction-nft/nanox.77.png new file mode 100644 index 00000000..cc0bd7b2 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.77.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.78.png b/tests_speculos/test-transaction-nft/nanox.78.png new file mode 100644 index 00000000..1e4be699 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.78.png differ diff --git a/tests_speculos/test-transaction-nft/nanox.79.png b/tests_speculos/test-transaction-nft/nanox.79.png new file mode 100644 index 00000000..dbbdb276 Binary files /dev/null and b/tests_speculos/test-transaction-nft/nanox.79.png differ diff --git a/tests_speculos/yarn-error.log b/tests_speculos/yarn-error.log new file mode 100644 index 00000000..90f02a6c --- /dev/null +++ b/tests_speculos/yarn-error.log @@ -0,0 +1,59 @@ +Arguments: + /home/robert/.nvm/versions/node/v16.10.0/bin/node /usr/local/bin/yarn install + +PATH: + /home/robert/ledgerflow/bin:/home/robert/.nvm/versions/node/v16.10.0/bin:/usr/bin:/home/robert/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin + +Yarn version: + 1.22.10 + +Node version: + 16.10.0 + +Platform: + linux x64 + +Trace: + SyntaxError: /home/robert/git/ledger-app-flow/tests_speculos/package.json: Unexpected token . in JSON at position 322 + at JSON.parse () + at /usr/local/lib/node_modules/yarn/lib/cli.js:1625:59 + at Generator.next () + at step (/usr/local/lib/node_modules/yarn/lib/cli.js:310:30) + at /usr/local/lib/node_modules/yarn/lib/cli.js:321:13 + +npm manifest: + { + "name": "speculos-integration-tests", + "version": "1.0.0", + "description": "", + "license": "Apache-2.0", + "main": "index.js", + "type": "module", + "dependencies": { + "@ledgerhq/hw-transport-node-speculos": "^6.20.0", + "@onflow/ledger": "file:../js", + "@onflow/encode": "^0.0.8", + "deepmerge": "^4.2.2". + "assert": "^2.0.0", + "strict": "^0.0.0" + }, + "devDependencies": { + "@babel/cli": "^7.11.6", + "@babel/core": "^7.11.6", + "@babel/node": "^7.10.5", + "@babel/plugin-transform-runtime": "^7.11.5", + "@babel/preset-env": "^7.11.5", + "@babel/runtime": "^7.10.5", + "@ledgerhq/hw-transport-node-hid": "^6.27.1", + "babel-eslint": "^10.1.0", + "elliptic": "^6.5.3", + "jssha": "^3.1.1", + "wait-console-input": "^0.1.7" + } + } + +yarn manifest: + No manifest + +Lockfile: + No lockfile