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

ICP support #605

Draft
wants to merge 16 commits into
base: develop
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion apps/btc_family/btc_txn.c
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ static bool fetch_valid_input(btc_query_t *query) {

verify_input_data.size_last_chunk = total_size % CHUNK_SIZE;
if (verify_input_data.size_last_chunk < 4) {
verify_input_data.isLocktimeSplit = true;
verify_input_data.is_locktime_split = true;
}
}

Expand Down
40 changes: 22 additions & 18 deletions apps/btc_family/btc_txn_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ static void update_hash(btc_verify_input_t *verify_input_data,
}
switch (update) {
case FIRST_CHUNK_HASH: {
if (verify_input_data->isSegwit) {
if (verify_input_data->is_segwit) {
sha256_Update(&(verify_input_data->sha_256_ctx), raw_txn_chunk, 4);
// skip marker and flag
sha256_Update(
Expand All @@ -361,7 +361,7 @@ static void update_hash(btc_verify_input_t *verify_input_data,
static void update_locktime(btc_verify_input_t *verify_input_data,
const uint8_t *raw_txn_chunk,
int chunk_index) {
if (verify_input_data->isLocktimeSplit) {
if (verify_input_data->is_locktime_split) {
// last second chunk
if (chunk_index + 2 == verify_input_data->chunk_total) {
memcpy(
Expand All @@ -374,7 +374,7 @@ static void update_locktime(btc_verify_input_t *verify_input_data,
verify_input_data->locktime + 4 - verify_input_data->size_last_chunk,
raw_txn_chunk,
verify_input_data->size_last_chunk);
verify_input_data->hasLocktime = true;
verify_input_data->has_locktime = true;
return;
} else {
// wait for subsequent chunks
Expand All @@ -384,7 +384,7 @@ static void update_locktime(btc_verify_input_t *verify_input_data,
memcpy(verify_input_data->locktime,
raw_txn_chunk + verify_input_data->size_last_chunk - 4,
4);
verify_input_data->hasLocktime = true;
verify_input_data->has_locktime = true;
} else {
// wait for subsequent chunks
return;
Expand Down Expand Up @@ -425,16 +425,18 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,
// ignore network version (4-bytes), skip marker & flag (in segwit)
offset += (raw_txn_chunk[4] == 0 ? 6 : 4);
if (6 == offset) {
verify_input_data->isSegwit = true;
verify_input_data->is_segwit = true;
}

// store the number of inputs in the raw_txn
verify_input_data->count = raw_txn_chunk[offset++];
// TODO: Improve varint decode.
// size of variable containing script size and ip-count/op-count
// varies (1-9 Bytes) depending on its value.
// refer:
// https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
verify_input_data->count =
raw_txn_chunk[offset++]; ///< store the number of inputs in the
///< raw_txn

verify_input_data->parsetype = INPUT;
sha256_Init(&(verify_input_data->sha_256_ctx));
} else {
Expand All @@ -447,7 +449,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,
input_case ip_case = verify_input_data->input_parse;
switch (ip_case) {
case PREVIOUS_TX_HASH_PLUS_OP_INDEX_CASE: {
if (offset + 36 > CHUNK_SIZE) {
if (offset + 36 >= CHUNK_SIZE) {
verify_input_data->prev_offset = (offset + 36) - CHUNK_SIZE;
update_hash(
verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE);
Expand All @@ -460,7 +462,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,

case SCRIPT_LENGTH_CASE: {
int64_t script_length = varint_decode(raw_txn_chunk, &offset);
if (offset + script_length + 1 + 4 > CHUNK_SIZE) {
if (offset + script_length + 1 + 4 >= CHUNK_SIZE) {
verify_input_data->prev_offset =
(offset + script_length + 1 + 4) - CHUNK_SIZE;
update_hash(
Expand All @@ -485,9 +487,11 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,
}

case OP_COUNT: {
if (offset + 1 > CHUNK_SIZE) {
if (offset + 1 >= CHUNK_SIZE) {
// reset prev offset
verify_input_data->prev_offset = -1;
verify_input_data->prev_offset =
offset - CHUNK_SIZE; ///< Did not add +1 as returning back to
///< this stage to read op count
update_hash(verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE);
return 4;
} else {
Expand All @@ -503,21 +507,21 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,
switch (op_case) {
case VALUE_CASE: {
if (verify_input_data->output_index == input->prev_output_index) {
if (offset + 8 > CHUNK_SIZE) {
if (offset + 8 >= CHUNK_SIZE) {
verify_input_data->prev_offset = (offset + 8) - CHUNK_SIZE;
memcpy(verify_input_data->value,
raw_txn_chunk + offset,
CHUNK_SIZE - offset);
update_hash(
verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE);
verify_input_data->output_parse = VALUE_SPLIT_CASE;
verify_input_data->isSplit = 1;
verify_input_data->is_split = 1;
return 4;
} else {
memcpy(verify_input_data->value, raw_txn_chunk + offset, 8);
}
}
if (offset + 8 > CHUNK_SIZE) {
if (offset + 8 >= CHUNK_SIZE) {
verify_input_data->prev_offset = (offset + 8) - CHUNK_SIZE;
update_hash(
verify_input_data, raw_txn_chunk, chunk_index, CHUNK_SIZE);
Expand All @@ -529,17 +533,17 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,
}

case VALUE_SPLIT_CASE: {
if (verify_input_data->isSplit) {
if (verify_input_data->is_split) {
memcpy(verify_input_data->value + (8 - offset),
raw_txn_chunk,
offset);
verify_input_data->output_parse = SCRIPT_PUBKEY_CASE;
verify_input_data->isSplit = 0;
verify_input_data->is_split = 0;
}
}

case SCRIPT_PUBKEY_CASE: {
if (offset + raw_txn_chunk[offset] + 1 > CHUNK_SIZE) {
if (offset + raw_txn_chunk[offset] + 1 >= CHUNK_SIZE) {
verify_input_data->prev_offset =
(offset + raw_txn_chunk[offset] + 1) - CHUNK_SIZE;
update_hash(
Expand All @@ -565,7 +569,7 @@ int btc_verify_input(const uint8_t *raw_txn_chunk,

case LOCK_TIME: {
update_locktime(verify_input_data, raw_txn_chunk, chunk_index);
if (false == verify_input_data->hasLocktime) {
if (false == verify_input_data->has_locktime) {
return 4;
}
sha256_Update(
Expand Down
8 changes: 4 additions & 4 deletions apps/btc_family/btc_txn_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ typedef struct btc_verify_input {
parse_type parsetype;
input_case input_parse;
output_case output_parse;
bool isSegwit;
bool isSplit;
bool hasLocktime;
bool isLocktimeSplit;
bool is_segwit;
bool is_split;
bool has_locktime;
bool is_locktime_split;
int32_t size_last_chunk;
uint8_t value[8];
uint8_t locktime[4];
Expand Down
196 changes: 196 additions & 0 deletions apps/icp_app/icp_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/**
* @file icp_api.c
* @author Cypherock X1 Team
* @brief Defines helpers apis for ICP app.
* @copyright Copyright (c) 2024 HODL TECH PTE LTD
* <br/> You may obtain a copy of license at <a href="https://mitcc.org/"
*target=_blank>https://mitcc.org/</a>
*
******************************************************************************
* @attention
*
* (c) Copyright 2024 by HODL TECH PTE LTD
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* "Commons Clause" License Condition v1.0
*
* The Software is provided to you by the Licensor under the License,
* as defined below, subject to the following condition.
*
* Without limiting other conditions in the License, the grant of
* rights under the License will not include, and the License does not
* grant to you, the right to Sell the Software.
*
* For purposes of the foregoing, "Sell" means practicing any or all
* of the rights granted to you under the License to provide to third
* parties, for a fee or other consideration (including without
* limitation fees for hosting or consulting/ support services related
* to the Software), a product or service whose value derives, entirely
* or substantially, from the functionality of the Software. Any license
* notice or attribution required by the License must also include
* this Commons Clause License Condition notice.
*
* Software: All X1Wallet associated files.
* License: MIT
* Licensor: HODL TECH PTE LTD
*
******************************************************************************
*/

/*****************************************************************************
* INCLUDES
*****************************************************************************/

#include "icp_api.h"

#include <pb_decode.h>
#include <pb_encode.h>

#include "common_error.h"
#include "core_api.h"
#include "events.h"

/*****************************************************************************
* EXTERN VARIABLES
*****************************************************************************/

/*****************************************************************************
* PRIVATE MACROS AND DEFINES
*****************************************************************************/

/*****************************************************************************
* PRIVATE TYPEDEFS
*****************************************************************************/

/*****************************************************************************
* STATIC VARIABLES
*****************************************************************************/

/*****************************************************************************
* GLOBAL VARIABLES
*****************************************************************************/

/*****************************************************************************
* STATIC FUNCTION PROTOTYPES
*****************************************************************************/

/*****************************************************************************
* STATIC FUNCTIONS
*****************************************************************************/

/*****************************************************************************
* GLOBAL FUNCTIONS
*****************************************************************************/
bool decode_icp_query(const uint8_t *data,
uint16_t data_size,
icp_query_t *query_out) {
if (NULL == data || NULL == query_out || 0 == data_size) {
icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG,
ERROR_DATA_FLOW_DECODING_FAILED);
return false;
}

// zeroise for safety from garbage in the query reference
memzero(query_out, sizeof(icp_query_t));

/* Create a stream that reads from the buffer. */
pb_istream_t stream = pb_istream_from_buffer(data, data_size);

/* Now we are ready to decode the message. */
bool status = pb_decode(&stream, ICP_QUERY_FIELDS, query_out);

/* Send error to host if status is false*/
if (false == status) {
icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG,
ERROR_DATA_FLOW_DECODING_FAILED);
}

return status;
}

bool encode_icp_result(const icp_result_t *result,
uint8_t *buffer,
uint16_t max_buffer_len,
size_t *bytes_written_out) {
if (NULL == result || NULL == buffer || NULL == bytes_written_out)
return false;

/* Create a stream that will write to our buffer. */
pb_ostream_t stream = pb_ostream_from_buffer(buffer, max_buffer_len);

/* Now we are ready to encode the message! */
bool status = pb_encode(&stream, ICP_RESULT_FIELDS, result);

if (true == status) {
*bytes_written_out = stream.bytes_written;
}

return status;
}

bool check_icp_query(const icp_query_t *query, pb_size_t exp_query_tag) {
if ((NULL == query) || (exp_query_tag != query->which_request)) {
icp_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG,
ERROR_DATA_FLOW_INVALID_QUERY);
return false;
}
return true;
}

icp_result_t init_icp_result(pb_size_t result_tag) {
icp_result_t result = ICP_RESULT_INIT_ZERO;
result.which_response = result_tag;
return result;
}

void icp_send_error(pb_size_t which_error, uint32_t error_code) {
icp_result_t result = init_icp_result(ICP_RESULT_COMMON_ERROR_TAG);
result.common_error = init_common_error(which_error, error_code);
icp_send_result(&result);
}

void icp_send_result(const icp_result_t *result) {
// TODO: Set all option files
uint8_t buffer[1700] = {0};
size_t bytes_encoded = 0;
ASSERT(encode_icp_result(result, buffer, sizeof(buffer), &bytes_encoded));
send_response_to_host(&buffer[0], bytes_encoded);
}

bool icp_get_query(icp_query_t *query, pb_size_t exp_query_tag) {
evt_status_t event = get_events(EVENT_CONFIG_USB, MAX_INACTIVITY_TIMEOUT);

if (true == event.p0_event.flag) {
return false;
}

if (!decode_icp_query(
event.usb_event.p_msg, event.usb_event.msg_size, query)) {
return false;
}

if (!check_icp_query(query, exp_query_tag)) {
return false;
}

return true;
}
Loading