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

Add Support for update contract transaction #78

Open
wants to merge 8 commits into
base: browser-wallet-support
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Added

* Support for update contract transaction.

## 4.0.1

### Changed
Expand Down
23 changes: 23 additions & 0 deletions doc/ins_update_contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Update contract

A transaction to update a contract instance.
The payload contains the following fields:

- amount: microCCD sent to the contract instance.
- index: contract instance index.
- subindex: contract instance subindex.
- receive name: `\<contractName\>.\<functionName\>`.
- parameter: Specific message for the contract / the paramaters for the function.

The length of the receive name and parameter is sent together with the remaining field values in the initial batch.
Note that that the receive name is required to have a length at least 1, and if parameters length is 0, the signature is returned on the last receiveName batch (i.e. P1 = 2 is skipped).

## Protocol description

* Multiple commands

INS | P1 | P2 | CDATA | Comment |
|----|--------|-----|-------------|----|
| `0x36` | `0x00` | `0x00` | `path_length path[uint32]x[8] account_transaction_header[60 bytes] transaction_kind[uint8] amount[uint64] index[uint64] subindex[uint64] receiveName length[uint16] parameter length[uint16]` | |
| `0x36` | `0x01` | `0x00` | `receiveName[1..255 bytes]` | In batches of up to 255 bytes until the entire receive name has been sent |
| `0x36` | `0x02` | `0x00` | `parameter[1..255 bytes]` | In batches of up to 255 bytes until the entire parameter has been sent |
67 changes: 67 additions & 0 deletions rust_tests/src/bin/update_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
mod account_transaction_header;
mod path;
use std::convert::TryFrom;

use hex;
use ledger::{ApduCommand, LedgerApp};

fn main() {
let ins = 54;
let transaction_kind = "02";

let mut amount = hex::decode("0000FFFFFFFFFFFF").unwrap();
let mut index = hex::decode("0000FFFFFFFFFFFF").unwrap();
let mut subindex = hex::decode("0000FFFFFFFFFFFF").unwrap();
let name = b"myReceiveFunction".to_vec();
let param = b"mySpicyParam".to_vec();

// Build transaction payload bytes.
let mut transaction_payload = hex::decode(transaction_kind).unwrap();
transaction_payload.append(&mut amount);
transaction_payload.append(&mut index);
transaction_payload.append(&mut subindex);
transaction_payload.append(&mut u16::try_from(name.len()).unwrap().to_be_bytes().to_vec());
transaction_payload.append(&mut u16::try_from(param.len()).unwrap().to_be_bytes().to_vec());

let mut command_data = path::generate_key_derivation_path();
command_data.append(&mut account_transaction_header::generate_account_transaction_header());
command_data.append(&mut transaction_payload);

let ledger = LedgerApp::new().unwrap();

let command1 = ApduCommand {
cla: 224,
ins,
p1: 0,
p2: 0,
length: command_data.len() as u8,
data: command_data
};
ledger.exchange(command1).expect("update contract, initial packet failed.");

for name_part in name.chunks(255) {
let name_command = ApduCommand {
cla: 224, // Has to be this value for all commands.
ins,
p1: 1,
p2: 0,
length: name_part.len() as u8,
data: name_part.to_vec()
};
ledger.exchange(name_command).expect("update contract, name packet failed.");
}


for param_part in param.chunks(255) {
let param_command = ApduCommand {
cla: 224, // Has to be this value for all commands.
ins,
p1: 2,
p2: 0,
length: param_part.len() as u8,
data: param_part.to_vec()
};
let result = ledger.exchange(param_command).expect("update contract, parameter packet failed.");
println!("Signature: {}", hex::encode(result.data));
}
}
3 changes: 3 additions & 0 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "signTransferToPublic.h"
#include "signTransferWithSchedule.h"
#include "signRegisterData.h"
#include "signUpdateContract.h"

#include "ux.h"

Expand Down Expand Up @@ -107,6 +108,8 @@ typedef union {
signConfigureBaker_t signConfigureBaker;
signConfigureDelegationContext_t signConfigureDelegation;

updateContractContext_t signUpdateContract;

transactionWithDataBlob_t withDataBlob;
} instructionContext;
extern instructionContext global;
Expand Down
6 changes: 6 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "signTransferToEncrypted.h"
#include "signTransferToPublic.h"
#include "signTransferWithSchedule.h"
#include "signUpdateContract.h"
#include "ux.h"
#include "verifyAddress.h"

Expand Down Expand Up @@ -90,6 +91,8 @@ accountSender_t global_account_sender;
#define INS_SIGN_TRANSFER_WITH_SCHEDULE_AND_MEMO 0x34
#define INS_REGISTER_DATA 0x35

#define INS_SIGN_UPDATE_CONTRACT 0x36

// Main entry of application that listens for APDU commands that will be received from the
// computer. The APDU commands control what flow is activated, i.e. which control flow is initiated.
static void concordium_main(void) {
Expand Down Expand Up @@ -189,6 +192,9 @@ static void concordium_main(void) {
case INS_SIGN_UPDATE_CREDENTIAL:
handleSignUpdateCredential(cdata, p1, p2, &flags, isInitialCall);
break;
case INS_SIGN_UPDATE_CONTRACT:
handleSignUpdateContract(cdata, p1, lc, &flags, isInitialCall);
break;
default:
THROW(ERROR_INVALID_INSTRUCTION);
break;
Expand Down
182 changes: 182 additions & 0 deletions src/signUpdateContract.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#include <os.h>

#include "accountSenderView.h"
#include "responseCodes.h"
#include "sign.h"
#include "util.h"

static updateContractContext_t *ctx = &global.signUpdateContract;
static tx_state_t *tx_state = &global_tx_state;

UX_STEP_NOCB(
ux_sign_update_contract_display_amount,
bnnn_paging,
{.title = "Amount (CCD)", .text = (char *) global.signUpdateContract.displayAmount});

UX_STEP_NOCB(
ux_sign_update_contract_display_index,
bnnn_paging,
{.title = "Contract index", .text = (char *) global.signUpdateContract.displayIndex});

UX_STEP_NOCB(
ux_sign_update_contract_display_subindex,
bnnn_paging,
{.title = "Contract subindex", .text = (char *) global.signUpdateContract.displaySubindex});

UX_STEP_NOCB(
ux_sign_update_contract_display_receive_name,
bnnn_paging,
{.title = "Receive name", .text = (char *) global.signUpdateContract.display});

UX_STEP_NOCB(
ux_sign_update_contract_display_parameter,
bnnn_paging,
{.title = "Parameter", .text = (char *) global.signUpdateContract.display});

UX_STEP_NOCB(ux_sign_update_contract_display_no_parameter, bn, {"No Parameter", ""});

UX_STEP_VALID(ux_sign_update_contract_continue, nn, sendSuccessNoIdle(), {"Continue", "with transaction"});

// Display parameter with continue
UX_FLOW(
ux_sign_update_contract_parameter,
&ux_sign_update_contract_display_parameter,
&ux_sign_update_contract_continue);
// Display parameter with finish
UX_FLOW(
ux_sign_update_contract_finish,
&ux_sign_update_contract_display_parameter,
&ux_sign_flow_shared_sign,
&ux_sign_flow_shared_decline);

const ux_flow_step_t *ux_sign_update_contract_flow[12];

/**
* Builds and inits the ux flow for the receiveName.
* prepends the initial batch of info and appends continue/sign pages based on the given parameters.
*/
void startUpdateContractDisplayReceiveName(bool displayStart, bool finalReceiveName, bool emptyParameter) {
uint8_t index = 0;
if (displayStart) {
ux_sign_update_contract_flow[index++] = &ux_sign_flow_shared_review;
ux_sign_update_contract_flow[index++] = &ux_sign_flow_account_sender_view;
ux_sign_update_contract_flow[index++] = &ux_sign_update_contract_display_amount;
ux_sign_update_contract_flow[index++] = &ux_sign_update_contract_display_index;
ux_sign_update_contract_flow[index++] = &ux_sign_update_contract_display_subindex;
}

ux_sign_update_contract_flow[index++] = &ux_sign_update_contract_display_receive_name;

if (finalReceiveName && emptyParameter) {
ux_sign_update_contract_flow[index++] = &ux_sign_update_contract_display_no_parameter;
ux_sign_update_contract_flow[index++] = &ux_sign_flow_shared_sign;
ux_sign_update_contract_flow[index++] = &ux_sign_flow_shared_decline;
} else {
ux_sign_update_contract_flow[index++] = &ux_sign_update_contract_continue;
}
ux_sign_update_contract_flow[index++] = FLOW_END_STEP;
ux_flow_init(0, ux_sign_update_contract_flow, NULL);
}

#define P1_INITIAL 0x00
#define P1_RECEIVE_NAME 0x01
#define P1_PARAMETER 0x02

void handleSignUpdateContract(
uint8_t *cdata,
uint8_t p1,
uint8_t dataLength,
volatile unsigned int *flags,
bool isInitialCall) {
if (isInitialCall) {
ctx->state = TX_UPDATE_CONTRACT_INITIAL;
}

if (p1 == P1_INITIAL && ctx->state == TX_UPDATE_CONTRACT_INITIAL) {
cdata += parseKeyDerivationPath(cdata);
cx_sha256_init(&tx_state->hash);
cdata += hashAccountTransactionHeaderAndKind(cdata, UPDATE);

// Build display value of the amount to transfer to the contract, and also add the bytes to the hash.
uint64_t amount = U8BE(cdata, 0);
amountToGtuDisplay(ctx->displayAmount, sizeof(ctx->displayAmount), amount);
cx_hash((cx_hash_t *) &tx_state->hash, 0, cdata, 8, NULL, 0);
cdata += 8;

// Build display value of the index of the contract, and also add the bytes to the hash.
uint64_t index = U8BE(cdata, 0);
amountToGtuDisplay(ctx->displayIndex, sizeof(ctx->displayIndex), index);
cx_hash((cx_hash_t *) &tx_state->hash, 0, cdata, 8, NULL, 0);
cdata += 8;

// Build display value of the subindex of the contract, and also add the bytes to the hash.
uint64_t subindex = U8BE(cdata, 0);
amountToGtuDisplay(ctx->displaySubindex, sizeof(ctx->displaySubindex), subindex);
cx_hash((cx_hash_t *) &tx_state->hash, 0, cdata, 8, NULL, 0);
cdata += 8;

// Save the length of the receiveName and add the bytes to the hash
ctx->nameLength = U2BE(cdata, 0);
cx_hash((cx_hash_t *) &tx_state->hash, 0, cdata, 2, NULL, 0);
cdata += 2;

// Save the length of the parameters
ctx->paramLength = U2BE(cdata, 0);
memmove(ctx->rawParameterLength, cdata, 2);

if (ctx->nameLength == 0) {
THROW(ERROR_INVALID_PARAM);
}

ctx->displayStart = true;
ctx->state = TX_UPDATE_CONTRACT_RECEIVE_NAME;
sendSuccessNoIdle();
} else if (p1 == P1_RECEIVE_NAME && ctx->state == TX_UPDATE_CONTRACT_RECEIVE_NAME) {
ctx->nameLength -= dataLength;
cx_hash((cx_hash_t *) &tx_state->hash, 0, cdata, dataLength, NULL, 0);

// Build display of receive name chunk
memmove(ctx->display, cdata, dataLength);
if (dataLength < 255) {
memmove(ctx->display + dataLength, "\0", 1);
}

if (ctx->nameLength < 0) {
THROW(ERROR_INVALID_STATE);
} else if (ctx->nameLength == 0) {
// Hash the parameterLength
cx_hash((cx_hash_t *) &tx_state->hash, 0, ctx->rawParameterLength, 2, NULL, 0);
ctx->state = TX_UPDATE_CONTRACT_PARAMETER;
}

startUpdateContractDisplayReceiveName(ctx->displayStart, ctx->nameLength == 0, ctx->paramLength == 0);

if (ctx->displayStart) {
ctx->displayStart = false;
}
*flags |= IO_ASYNCH_REPLY;
} else if (p1 == P1_PARAMETER && ctx->state == TX_UPDATE_CONTRACT_PARAMETER) {
ctx->paramLength -= dataLength;
cx_hash((cx_hash_t *) &tx_state->hash, 0, cdata, dataLength, NULL, 0);

// Build display of parameter chunk
memmove(ctx->display, cdata, dataLength);
if (dataLength < 255) {
memmove(ctx->display + dataLength, "\0", 1);
}

if (ctx->paramLength < 0) {
THROW(ERROR_INVALID_PARAM);
} else if (ctx->paramLength == 0) {
ctx->state = TX_UPDATE_CONTRACT_RECEIVE_NAME;

ux_flow_init(0, ux_sign_update_contract_finish, NULL);
} else {
ux_flow_init(0, ux_sign_update_contract_parameter, NULL);
sendSuccessNoIdle();
}
*flags |= IO_ASYNCH_REPLY;
} else {
THROW(ERROR_INVALID_STATE);
}
}
35 changes: 35 additions & 0 deletions src/signUpdateContract.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef _CONCORDIUM_APP_ACCOUNT_UPDATE_CONTRACT_H_
#define _CONCORDIUM_APP_ACCOUNT_UPDATE_CONTRACT_H_

/**
* Handles the signing flow, including updating the display, for the 'update contract'
* account transaction.
* @param cdata please see /doc/ins_update_contract.md for details
*/
void handleSignUpdateContract(
uint8_t *cdata,
uint8_t p1,
uint8_t dataLength,
volatile unsigned int *flags,
bool isInitialCall);

typedef enum {
TX_UPDATE_CONTRACT_INITIAL = 100,
TX_UPDATE_CONTRACT_RECEIVE_NAME = 101,
TX_UPDATE_CONTRACT_PARAMETER = 102,
} updateContractState_t;

typedef struct {
// For receiveName and parameters
uint8_t display[255];
uint8_t displayAmount[26];
uint8_t displayIndex[26];
uint8_t displaySubindex[26];
uint16_t nameLength;
uint8_t rawParameterLength[2];
uint8_t paramLength;
bool displayStart;
updateContractState_t state;
} updateContractContext_t;

#endif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading