Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/LDG-537--nano-app-implement-initialize-method #40

Merged
merged 8 commits into from
Dec 11, 2024
3 changes: 3 additions & 0 deletions src/common/handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ int handler(uint8_t INS,
case INS_DEPLOY_MODULE:
handleDeployModule(cdata, p1, lc);
break;
case INS_INIT_CONTRACT:
handleInitContract(cdata, p1, lc);
break;
default:
THROW(ERROR_INVALID_INSTRUCTION);
break;
Expand Down
2 changes: 2 additions & 0 deletions src/common/handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#define INS_DEPLOY_MODULE 0x06

#define INS_INIT_CONTRACT 0x07

#define INS_ENCRYPTED_AMOUNT_TRANSFER 0x10
#define INS_TRANSFER_TO_ENCRYPTED 0x11
#define INS_TRANSFER_TO_PUBLIC 0x12
Expand Down
4 changes: 3 additions & 1 deletion src/common/responseCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ enum {
ERROR_FAILED_CX_OPERATION = 0x6B07,
ERROR_INVALID_INSTRUCTION = 0x6D00,
ERROR_INVALID_SOURCE_LENGTH = 0x6B08,

ERROR_INVALID_NAME_LENGTH = 0x6B0A,
ERROR_INVALID_PARAMS_LENGTH = 0x6B0B,
ERROR_INVALID_MODULE_REF = 0x6B09,
// Error codes from the Ledger firmware
ERROR_DEVICE_LOCKED = 0x530C,
SW_WRONG_DATA_LENGTH = 0x6A87
Expand Down
1 change: 1 addition & 0 deletions src/common/ui/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,4 @@ void uiSignScheduledTransferPairFlowSignDisplay(void);
void uiSignScheduledTransferPairFlowDisplay(void);

void uiDeployModuleDisplay(void);
void uiInitContractDisplay(void);
19 changes: 19 additions & 0 deletions src/common/ui/display_bagl.c
Original file line number Diff line number Diff line change
Expand Up @@ -813,4 +813,23 @@ void uiDeployModuleDisplay() {
ux_flow_init(0, ux_deploy_module, NULL);
}

// Init Contract
UX_STEP_NOCB(ux_init_contract_1_step,
bnnn_paging,
{.title = "Amount", .text = (char *) global.initContract.amountDisplay});
UX_STEP_NOCB(ux_init_contract_2_step,
bnnn_paging,
{.title = "Module ref", .text = (char *) global.initContract.moduleRefDisplay});
UX_FLOW(ux_init_contract,
&ux_sign_flow_shared_review,
&ux_sign_flow_account_sender_view,
&ux_init_contract_1_step,
&ux_init_contract_2_step,
&ux_sign_flow_shared_sign,
&ux_sign_flow_shared_decline);

void uiInitContractDisplay() {
ux_flow_init(0, ux_init_contract, NULL);
}

#endif
2 changes: 2 additions & 0 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "signTransferWithSchedule.h"
#include "signRegisterData.h"
#include "deployModule.h"
#include "initContract.h"
#include "ux.h"

#define LEGACY_PURPOSE 1105
Expand Down Expand Up @@ -125,6 +126,7 @@ typedef union {
signConfigureBaker_t signConfigureBaker;
signConfigureDelegationContext_t signConfigureDelegation;
deployModule_t deployModule;
initContract_t initContract;
transactionWithDataBlob_t withDataBlob;
} instructionContext;
extern instructionContext global;
Expand Down
93 changes: 93 additions & 0 deletions src/initContract.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "os.h"
#include "format.h"
#include "common/ui/display.h"
#include "common/responseCodes.h"
#include "common/sign.h"
#include "common/util.h"
#include "initContract.h"

// TODO: ADAPT THIS TO THE NEW INSTRUCTION

static initContract_t *ctx_init_contract = &global.initContract;
static tx_state_t *tx_state = &global_tx_state;

#define P1_INITIAL 0x00
#define P1_NAME 0x01
#define P1_PARAMS 0x02

void handleInitContract(uint8_t *cdata, uint8_t p1, uint8_t lc) {
if (p1 == P1_INITIAL) {
cx_sha256_init(&tx_state->hash);
cdata += parseKeyDerivationPath(cdata);
cdata += hashAccountTransactionHeaderAndKind(cdata, INIT_CONTRACT);
// hash the amount
updateHash((cx_hash_t *) &tx_state->hash, cdata, 8);
// extract the amount
ctx_init_contract->amount = U8BE(cdata, 0);
// Format the amount
amountToGtuDisplay((uint8_t *) ctx_init_contract->amountDisplay,
sizeof(ctx_init_contract->amountDisplay),
ctx_init_contract->amount);
cdata += 8;
// hash the module ref
updateHash((cx_hash_t *) &tx_state->hash, cdata, 32);
// extract the module ref
memmove(ctx_init_contract->moduleRef, cdata, 32);
// Format the module ref
if (format_hex(ctx_init_contract->moduleRef, 32, ctx_init_contract->moduleRefDisplay, 65) ==
-1) {
THROW(ERROR_INVALID_MODULE_REF);
}
ctx_init_contract->state = INIT_CONTRACT_NAME_FIRST;
sendSuccessNoIdle();
}

else if (p1 == P1_NAME) {
uint8_t lengthSize = 2;
if (ctx_init_contract->state == INIT_CONTRACT_NAME_FIRST) {
// extract the name length
ctx_init_contract->nameLength = U2BE(cdata, 0);
// calculate the remaining name length
ctx_init_contract->remainingNameLength = ctx_init_contract->nameLength + lengthSize;
// set the state to the next state
ctx_init_contract->state = INIT_CONTRACT_NAME_NEXT;
} else if (ctx_init_contract->remainingNameLength < lc) {
THROW(ERROR_INVALID_NAME_LENGTH);
}
// hash the whole chunk
updateHash((cx_hash_t *) &tx_state->hash, cdata, lc);
// subtract the length of the chunk from the remaining name length
ctx_init_contract->remainingNameLength -= lc;
if (ctx_init_contract->remainingNameLength > 0) {
sendSuccessNoIdle();
} else if (ctx_init_contract->remainingNameLength == 0) {
ctx_init_contract->state = INIT_CONTRACT_PARAMS_FIRST;
sendSuccessNoIdle();
}

} else if (p1 == P1_PARAMS) {
uint8_t lengthSize = 2;
if (ctx_init_contract->state == INIT_CONTRACT_PARAMS_FIRST) {
// extract the params length
ctx_init_contract->paramsLength = U2BE(cdata, 0);
// calculate the remaining params length
ctx_init_contract->remainingParamsLength = ctx_init_contract->paramsLength + lengthSize;
// set the state to the next state
ctx_init_contract->state = INIT_CONTRACT_PARAMS_NEXT;
} else if (ctx_init_contract->remainingParamsLength < lc) {
THROW(ERROR_INVALID_PARAMS_LENGTH);
}
// hash the whole chunk
updateHash((cx_hash_t *) &tx_state->hash, cdata, lc);
// subtract the length of the chunk from the remaining params length
ctx_init_contract->remainingParamsLength -= lc;
if (ctx_init_contract->remainingParamsLength > 0) {
sendSuccessNoIdle();
} else if (ctx_init_contract->remainingParamsLength == 0) {
uiInitContractDisplay();
}

} else {
THROW(ERROR_INVALID_STATE);
}
}
39 changes: 39 additions & 0 deletions src/initContract.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <stdint.h>
#include <stdbool.h>

// TODO: ADAPT THIS TO THE NEW INSTRUCTION

typedef enum {
INIT_CONTRACT_INITIAL = 60,
INIT_CONTRACT_NAME_FIRST = 61,
INIT_CONTRACT_NAME_NEXT = 62,
INIT_CONTRACT_PARAMS_FIRST = 63,
INIT_CONTRACT_PARAMS_NEXT = 64,
INIT_CONTRACT_END = 65
} initContractState_t;

/**
* Handles the INIT_CONTRACT instruction, which initializes a contract
*
*
*/
void handleInitContract(uint8_t *cdata, uint8_t p1, uint8_t lc);

typedef struct {
uint64_t amount;
uint8_t moduleRef[32];
char amountDisplay[21];
char moduleRefDisplay[65];
uint32_t nameLength;
uint32_t remainingNameLength;
uint32_t paramsLength;
uint32_t remainingParamsLength;
initContractState_t state;
} initContract_t;

// typedef struct {
// uint8_t version[32];
// uint8_t sourceLength[32];
// } deployModuleBlob_t;
Comment on lines +36 to +39

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
92 changes: 92 additions & 0 deletions tests/application_client/boilerplate_command_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class P1(IntEnum):
# Parameter 1 for deploy module
P1_DEPLOY_MODULE_INITIAL = 0x00
P1_DEPLOY_MODULE_SOURCE = 0x01
# Parameter 1 for init contract
P1_INIT_CONTRACT_INITIAL = 0x00
P1_INIT_CONTRACT_NAME = 0x01
P1_INIT_CONTRACT_PARAMS = 0x02


class P2(IntEnum):
Expand Down Expand Up @@ -83,6 +87,7 @@ class InsType(IntEnum):
CREDENTIAL_DEPLOYMENT = 0x04
EXPORT_PRIVATE_KEY = 0x05
DEPLOY_MODULE = 0x06
INIT_CONTRACT = 0x07
ENCRYPTED_AMOUNT_TRANSFER = 0x10
TRANSFER_TO_ENCRYPTED = 0x11
TRANSFER_TO_PUBLIC = 0x12
Expand Down Expand Up @@ -791,5 +796,92 @@ def deploy_module(
) as response:
yield response

@contextmanager
def init_contract(
self,
path: str,
header_and_type: bytes,
amount: int,
module_ref: bytes,
name: bytes,
params: bytes,
) -> Generator[None, None, None]:
print("km-------- Starting init_contract")
print(f"km-------- Path: {path}")
print(f"km-------- Amount: {amount}")
print(f"km-------- Module ref: {module_ref.hex()}")
print(f"km-------- Name: {name}")
print(f"km-------- Params: {params.hex()}")

if amount > 0xFFFFFFFFFFFFFFFF:
raise ValueError("Amount must be less than 18446744073709551615")
# Send the initial data
data = pack_derivation_path(path)
data += header_and_type
data += amount.to_bytes(8, byteorder="big")
data += module_ref
print(f"km-------- Initial data: {data.hex()}")
temp_response = self.backend.exchange(
cla=CLA,
ins=InsType.INIT_CONTRACT,
p1=P1.P1_INIT_CONTRACT_INITIAL,
p2=P2.P2_NONE,
data=data,
)
if temp_response.status != 0x9000:
raise ExceptionRAPDU(temp_response.status)
# Send the name
data = len(name).to_bytes(2, byteorder="big")
name_chunks = split_message(name, MAX_APDU_LEN)
print(f"km-------- Name chunks: {len(name_chunks)}")
for i, chunk in enumerate(name_chunks):
if i == 0:
data += chunk
keiff3r marked this conversation as resolved.
Show resolved Hide resolved
else:
data == chunk
print(f"km-------- Sending name chunk: {data.hex()}")
temp_response = self.backend.exchange(
cla=CLA,
ins=InsType.INIT_CONTRACT,
p1=P1.P1_INIT_CONTRACT_NAME,
p2=P2.P2_NONE,
data=data,
)
if temp_response.status != 0x9000:
raise ExceptionRAPDU(temp_response.status)
# Send the params
data = len(params).to_bytes(2, byteorder="big")
params_chunks = split_message(params, MAX_APDU_LEN)
last_chunk = params_chunks.pop()
print(f"km-------- Params chunks: {len(params_chunks) + 1}")
for i, chunk in enumerate(params_chunks):
if i == 0:
data += chunk
else:
data == chunk
print(f"km-------- Sending params chunk: {data.hex()}")
temp_response = self.backend.exchange(
cla=CLA,
ins=InsType.INIT_CONTRACT,
p1=P1.P1_INIT_CONTRACT_PARAMS,
p2=P2.P2_NONE,
data=data,
)
if temp_response.status != 0x9000:
raise ExceptionRAPDU(temp_response.status)
if len(params_chunks) == 0:
data = len(last_chunk).to_bytes(2, byteorder="big") + last_chunk
else:
data = last_chunk
print(f"km-------- Sending final params chunk: {data.hex()}")
with self.backend.exchange_async(
cla=CLA,
ins=InsType.INIT_CONTRACT,
p1=P1.P1_INIT_CONTRACT_PARAMS,
p2=P2.P2_NONE,
data=data,
) as response:
yield response

def get_async_response(self) -> Optional[RAPDU]:
return self.backend.last_async_response
Binary file added tests/snapshots/nanosp/test_init_contract/00000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00001.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00002.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00003.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00004.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00005.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00006.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanosp/test_init_contract/00007.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00001.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00002.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00003.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00004.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00005.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00006.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/snapshots/nanox/test_init_contract/00007.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions tests/test_init_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest

from application_client.boilerplate_command_sender import (
BoilerplateCommandSender,
Errors,
)
from application_client.boilerplate_response_unpacker import (
unpack_get_public_key_response,
)
from ragger.bip import calculate_public_key_and_chaincode, CurveChoice
from ragger.error import ExceptionRAPDU
from ragger.navigator import NavInsID, NavIns
from ragger.firmware import Firmware
from utils import navigate_until_text_and_compare, instructions_builder


@pytest.mark.active_test_scope
def test_init_contract(
backend, firmware, navigator, test_name, default_screenshot_path
):
client = BoilerplateCommandSender(backend)
path = "m/1105/0/0/0/0/2/0/0"
header_and_type = bytes.fromhex(
"20a845815bd43a1999e90fbf971537a70392eb38f89e6bd32b3dd70e1a9551d7000000000000000a0000000000000064000000290000000063de5da701"
)
amount = 0xFFFFFFFFFFFFFFFF
module_ref = bytes.fromhex(
"a00000000000000000000000000000000000000000000000000000000000000a"
)
name = bytes.fromhex("696e69745f5465737420436f6e7472616374")
params = bytes.fromhex("0102030405060708090a")

with client.init_contract(
path=path,
header_and_type=header_and_type,
amount=amount,
module_ref=module_ref,
name=name,
params=params,
):
navigate_until_text_and_compare(
firmware, navigator, "Sign", default_screenshot_path, test_name
)
response = client.get_async_response()
print(response.data.hex())
assert response.status == 0x9000
assert response.data == bytes.fromhex(
"ca0e947d521063cc40c3bf8a65dd05a6bb66c799957417f94123c0642162020fed8dd80cd9aadd51f24d697bb9bcce26b72115dbd80bfe893a8395b54f8bb10c"
)
Loading