diff --git a/CMakeLists.txt b/CMakeLists.txt index 638e2e1b..7c8bc6bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,7 @@ file(GLOB_RECURSE LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl_common.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl_txn.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl_masp.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/leb128.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/txn_validator.c diff --git a/app/src/parser_impl_common.h b/app/src/parser_impl_common.h index 3c2925b5..39594e67 100644 --- a/app/src/parser_impl_common.h +++ b/app/src/parser_impl_common.h @@ -81,6 +81,14 @@ extern "C" { #define CHANGE_VALIDATOR_METADATA_NORMAL_PARAMS 2 #define CHANGE_VALIDATOR_METADATA_EXPERT_PARAMS 7 +#define DISCRIMINANT_DATA 0x00 +#define DISCRIMINANT_EXTRA_DATA 0x01 +#define DISCRIMINANT_CODE 0x02 +#define DISCRIMINANT_SIGNATURE 0x03 +#define DISCRIMINANT_CIPHERTEXT 0x04 +#define DISCRIMINANT_MASP_TX 0x05 +#define DISCRIMINANT_MASP_BUILDER 0x06 + parser_error_t readByte(parser_context_t *ctx, uint8_t *byte); parser_error_t readBytes(parser_context_t *ctx, const uint8_t **output, uint16_t outputLen); parser_error_t readUint16(parser_context_t *ctx, uint16_t *value); diff --git a/app/src/parser_impl_masp.c b/app/src/parser_impl_masp.c new file mode 100644 index 00000000..4071754d --- /dev/null +++ b/app/src/parser_impl_masp.c @@ -0,0 +1,220 @@ +/******************************************************************************* + * (c) 2018 - 2023 Zondax AG + * + * 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 "parser_impl_masp.h" + +#include "parser_impl_common.h" +#include "parser_txdef.h" + +#define ASSET_ID_LEN 32 +#define ANCHOR_LEN 32 +#define TRANSPARENT_ADDRESS_LEN 20 +#define SHIELDED_OUTPUTS_LEN 756 +#define INT_128_LEN 16 +#define ZKPROFF_LEN 192 +#define AUTH_SIG_LEN 64 +#define SHIELDED_SPENDS_LEN 96 +#define SHIELDED_CONVERTS_LEN 32 + +static parser_error_t parse_sapling_bundle(parser_context_t *ctx, + masp_sapling_bundle_t *bundle) { + if (ctx == NULL || bundle == NULL) { + return parser_unexpected_error; + } + uint8_t i = 0; + + uint8_t has_shielded_spends = 0; + CHECK_ERROR(readByte(ctx, &has_shielded_spends)) + if (has_shielded_spends != 0) { + bundle->n_shileded_spends = has_shielded_spends; + bundle->shielded_spends.len = SHIELDED_SPENDS_LEN; + for (i = 0; i < bundle->n_shileded_spends; i++) { + CHECK_ERROR(readBytes(ctx, bundle->shielded_spends.ptr, + bundle->shielded_spends.len)) + } + } + + uint8_t has_shielded_converts = 0; + CHECK_ERROR(readByte(ctx, &has_shielded_converts)) + if (has_shielded_converts != 0) { + bundle->n_shielded_converts = has_shielded_converts; + bundle->shielded_converts.len = SHIELDED_CONVERTS_LEN; + for (i = 0; i < bundle->n_shielded_converts; i++) { + CHECK_ERROR(readBytes(ctx, bundle->shielded_converts.ptr, + bundle->shielded_converts.len)) + } + } + + uint8_t has_shielded_outputs = 0; + CHECK_ERROR(readByte(ctx, &has_shielded_outputs)) + if (has_shielded_outputs != 0) { + bundle->n_shielded_outputs = has_shielded_outputs; + bundle->shielded_outputs.len = SHIELDED_OUTPUTS_LEN; + for (i = 0; i < bundle->n_shielded_outputs; i++) { + CHECK_ERROR(readBytes(ctx, bundle->shielded_outputs.ptr, + bundle->shielded_outputs.len)) + } + } + + if (has_shielded_converts != 0 && has_shielded_outputs != 0) { + uint8_t has_value = 0; + CHECK_ERROR(readByte(ctx, &has_value)) + if (!has_value) { + return parser_unexpected_value; + } + bundle->value_asset_id.len = ASSET_ID_LEN; + CHECK_ERROR( + readBytes(ctx, bundle->value_asset_id.ptr, bundle->value_asset_id.len)) + + bundle->value_balance.len = INT_128_LEN; + CHECK_ERROR( + readBytes(ctx, &bundle->value_balance, bundle->value_balance.len)) + } + + if (has_shielded_spends != 0) { + bundle->anchor_shielded_spends.len = ANCHOR_LEN; + CHECK_ERROR(readBytes(ctx, bundle->anchor_shielded_spends.ptr, + bundle->anchor_shielded_spends.len)) + } + + if (has_shielded_converts != 0) { + bundle->anchor_shielded_converts.len = ANCHOR_LEN; + CHECK_ERROR(readBytes(ctx, bundle->anchor_shielded_converts.ptr, + bundle->anchor_shielded_converts.len)) + } + + if (has_shielded_spends != 0) { + bundle->zkproof_shielded_spends.len = ZKPROFF_LEN; + CHECK_ERROR(readBytes(ctx, bundle->zkproof_shielded_spends.ptr, + bundle->zkproof_shielded_spends.len)) + bundle->auth_sig_shielded_spends.len = AUTH_SIG_LEN; + CHECK_ERROR(readBytes(ctx, bundle->auth_sig_shielded_spends.ptr, + bundle->auth_sig_shielded_spends.len)) + } + + if (has_shielded_converts != 0) { + bundle->zkproof_shielded_converts.len = ZKPROFF_LEN; + CHECK_ERROR(readBytes(ctx, bundle->zkproof_shielded_converts.ptr, + bundle->zkproof_shielded_converts.len)) + } + + if (has_shielded_outputs != 0) { + bundle->zkproof_shielded_spends.len = ZKPROFF_LEN; + CHECK_ERROR(readBytes(ctx, bundle->zkproof_shielded_spends.ptr, + bundle->zkproof_shielded_spends.len)) + } + + if (has_shielded_converts != 0 && has_shielded_outputs != 0 && + has_shielded_spends != 0) { + bundle->authorization.len = AUTH_SIG_LEN; + CHECK_ERROR( + readBytes(ctx, bundle->authorization.ptr, bundle->authorization.len)) + } + + return parser_ok; +} + +static parser_error_t parse_transparent_bundle( + parser_context_t *ctx, masp_transparent_bundle_t *bundle) { + if (ctx == NULL || bundle == NULL) { + return parser_unexpected_error; + } + + uint8_t has_vin = 0; + CHECK_ERROR(readByte(ctx, &has_vin)) + + if (has_vin != 0) { + bundle->vin.asset_type_id.len = ASSET_ID_LEN; + CHECK_ERROR(readBytes(ctx, bundle->vin.asset_type_id.ptr, ASSET_ID_LEN)) + + // Nonce is not encoded in the bundle, skipped in borsh encoding + + CHECK_ERROR(readUint64(ctx, &bundle->vin.value)) + + bundle->vin.transparent_address.len = TRANSPARENT_ADDRESS_LEN; + CHECK_ERROR(readBytes(ctx, bundle->vin.transparent_address.ptr, + TRANSPARENT_ADDRESS_LEN)) + } + + uint8_t has_vout = 0; + CHECK_ERROR(readByte(ctx, &has_vout)) + + if (has_vout != 0) { + bundle->vout.asset_type_id.len = ASSET_ID_LEN; + CHECK_ERROR(readBytes(ctx, bundle->vout.asset_type_id.ptr, ASSET_ID_LEN)) + + // Nonce is not encoded in the bundle, skipped in borsh encoding + + CHECK_ERROR(readUint64(ctx, &bundle->vout.value)) + + bundle->vin.transparent_address.len = TRANSPARENT_ADDRESS_LEN; + CHECK_ERROR(readBytes(ctx, bundle->vout.transparent_address.ptr, + TRANSPARENT_ADDRESS_LEN)) + } + + return parser_ok; +} + +parser_error_t readMaspTx(parser_context_t *ctx, masp_tx_section_t *maspTx) { + if (ctx == NULL || maspTx == NULL) { + return parser_unexpected_error; + } + + uint8_t sectionMaspTx = 0; + CHECK_ERROR(readByte(ctx, §ionMaspTx)) + if (sectionMaspTx != DISCRIMINANT_MASP_TX) { + return parser_unexpected_value; + } + + // Read version for now only MaspV5 that translates to 0x02 + CHECK_ERROR(readUint32(ctx, &maspTx->data.tx_version)) + if (maspTx->data.tx_version != MASPV5_TX_VERSION) { + return parser_unexpected_value; + } + CHECK_ERROR(readUint32(ctx, &maspTx->data.version_group_id)) + if (maspTx->data.version_group_id != MASPV5_VERSION_GROUP_ID) { + return parser_unexpected_value; + } + + // Read branch id, unique enum for now + CHECK_ERROR(readUint32(ctx, &maspTx->data.consensus_branch_id)) + if (maspTx->data.consensus_branch_id != BRANCH_ID_IDENTIFIER) { + return parser_unexpected_value; + } + + // Read lock time + CHECK_ERROR(readUint32(ctx, &maspTx->data.lock_time)) + + // Read expiry_height + CHECK_ERROR(readUint32(ctx, &maspTx->data.expiry_height)) + + CHECK_ERROR(readUint16(ctx, &maspTx->data.has_transparent_bundle)) + if (maspTx->data.has_transparent_bundle != 0) { + ctx->offset -= 2; + parse_transparent_bundle(ctx, &maspTx->data.transparent_bundle); + } + uint8_t has_shielded_spends = 0; + uint8_t has_shielded_output = 0; + uint8_t has_shielded_converts = 0; + CHECK_ERROR(readByte(ctx, &has_shielded_spends)) + CHECK_ERROR(readByte(ctx, &has_shielded_converts)) + CHECK_ERROR(readByte(ctx, &has_shielded_output)) + if (has_shielded_spends != 0 && has_shielded_converts != 0 && + has_shielded_output != 0) { + ctx->offset -= 3; + parse_sapling_bundle(ctx, &maspTx->data.sapling_bundle); + } + return parser_ok; +} diff --git a/app/src/parser_impl_masp.h b/app/src/parser_impl_masp.h new file mode 100644 index 00000000..cbf56e8b --- /dev/null +++ b/app/src/parser_impl_masp.h @@ -0,0 +1,34 @@ +/******************************************************************************* +* (c) 2018 - 2022 Zondax AG +* +* 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. +********************************************************************************/ +#pragma once +#include "parser_common.h" +#include +#include "zxtypes.h" +#include "parser_txdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MASPV5_TX_VERSION 0x02 +#define MASPV5_VERSION_GROUP_ID 0x26A7270A +#define BRANCH_ID_IDENTIFIER 0xE9FF75A6 + +parser_error_t readMaspTx(parser_context_t *ctx, masp_tx_section_t *maspTx); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/parser_impl_txn.c b/app/src/parser_impl_txn.c index aac7d407..e638c718 100644 --- a/app/src/parser_impl_txn.c +++ b/app/src/parser_impl_txn.c @@ -15,6 +15,7 @@ ********************************************************************************/ #include "parser_impl_common.h" #include "parser_txdef.h" +#include "parser_impl_masp.h" #include "crypto_helper.h" #include "leb128.h" #include "bech32.h" @@ -24,14 +25,6 @@ #include "stdbool.h" #include -#define DISCRIMINANT_DATA 0x00 -#define DISCRIMINANT_EXTRA_DATA 0x01 -#define DISCRIMINANT_CODE 0x02 -#define DISCRIMINANT_SIGNATURE 0x03 -#define DISCRIMINANT_CIPHERTEXT 0x04 -#define DISCRIMINANT_MASP_TX 0x05 -#define DISCRIMINANT_MASP_BUILDER 0x06 - // Update VP types static const vp_types_t vp_user = { "vp_user.wasm", "User"}; static const vp_types_t vp_validator = { "vp_validator.wasm", "Validator"}; @@ -1084,26 +1077,6 @@ static parser_error_t readCodeSection(parser_context_t *ctx, section_t *code) { return parser_ok; } -#if(0) -static parser_error_t readCiphertext(parser_context_t *ctx, section_t *ciphertext) { - (void) ctx; - (void) ciphertext; - return parser_ok; -} - - -static parser_error_t readMaspTx(parser_context_t *ctx, section_t *maspTx) { - ctx->offset += 1171; // <- Transfer 2 // Transfer 1 -> 2403;//todo figure out correct number, fix this hack - (void) maspTx; - return parser_ok; -} - -static parser_error_t readMaspBuilder(parser_context_t *ctx, section_t *maspBuilder) { - ctx->offset += 941; // <- Transfer 2 // Transfer 1 -> 3060; //todo figure out correct number, fix this hack - (void) maspBuilder; - return parser_ok; -} -#endif parser_error_t readSections(parser_context_t *ctx, parser_tx_t *v) { if (ctx == NULL || v == NULL) { return parser_unexpected_value; @@ -1151,9 +1124,8 @@ parser_error_t readSections(parser_context_t *ctx, parser_tx_t *v) { signature->idx = i+1; break; } -#if(0) case DISCRIMINANT_CIPHERTEXT: - CHECK_ERROR(readCiphertext(ctx, &v->transaction.sections.ciphertext)) + //CHECK_ERROR(readCiphertext(ctx, &v->transaction.sections.ciphertext)) break; case DISCRIMINANT_MASP_TX: @@ -1161,9 +1133,9 @@ parser_error_t readSections(parser_context_t *ctx, parser_tx_t *v) { break; case DISCRIMINANT_MASP_BUILDER: - CHECK_ERROR(readMaspBuilder(ctx, &v->transaction.sections.maspBuilder)) + //CHECK_ERROR(readMaspBuilder(ctx, &v->transaction.sections.maspBuilder)) break; -#endif + default: return parser_unexpected_field; } diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index 50c4237b..76ad5f7a 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -66,49 +66,75 @@ typedef struct { uint32_t signaturesLen; bytes_t indexedSignatures; } signature_section_t; -#if(0) + typedef struct { bytes_t cv; // 160 bytes: Extended Point, i.e. 5 elements in Fq, each of which are represented by 32 bytes bytes_t anchor; // 32 bytes: bls12_381::Scalar bytes_t nullifier; // 32 bytes: [u8; 32] bytes_t rk; // 160 bytes: Extended Point, i.e. 5 elements in Fq, each of which are represented by 32 bytes_t zkproof; // [u8; GROTH_PROOF_SIZE] where GROTH_PROOF_SSIZE = 48 + 96 + 48 = 192 - bytes_t spend_auth_sig; // 64 bytes: rbar: [u8; 32], sbar: [u8; 32], } spend_description_t; // 640 bytes + typedef struct { - bytes_t cv; // 160 bytes: Extended Point, i.e. 5 elements in Fq, each of which are represented by 32 bytes + bytes_t cv; // 32 bytes: Extended Point, i.e. 5 elements in Fq, each of which are represented by 32 bytes +} convert_description_t; // 32 bytes + +// For inner parsing use +typedef struct { + bytes_t cv; // 32 bytes: Extended Point, i.e. 5 elements in Fq, each of which are represented by 32 bytes represented by hash? bytes_t cmu; // 32 bytes: bls12_381::Scalar bytes_t ephemeral_key; // 32 bytes: [u8; 32] -} output_description_t; // 224 bytes + bytes_t enc_ciphertext; // 580 bytes: [u8; 580] + bytes_t out_ciphertext; // 80 bytes: [u8; 80] +} output_description_t; // 756 bytes typedef struct { - spend_description_t* shielded_spends; - bytes_t shielded_converts; - output_description_t* shielded_outputs; - uint64_t value_balance; - bytes_t authorization; - // nothing? or (unauth) a Vec - // for shielded a redjubjub::Signature + uint8_t n_shileded_spends; + uint8_t n_shielded_converts; + uint8_t n_shielded_outputs; + bytes_t shielded_spends; // get complete vector + bytes_t shielded_converts; // get complete vector + bytes_t shielded_outputs; // get complete vector + + bytes_t value_asset_id; // [u8; 32] + bytes_t value_balance; // 8 bytes + + bytes_t anchor_shielded_spends; // 32 bytes: bls12_381::Scalar + bytes_t anchor_shielded_converts; // 32 bytes: bls12_381::Scalar + + bytes_t zkproof_shielded_spends; // [u8; GROTH_PROOF_SIZE] where GROTH_PROOF_SSIZE = 48 + 96 + 48 = 192 + bytes_t auth_sig_shielded_spends; // 64 bytes: rbar: [u8; 32], sbar: [u8; 32], + bytes_t zkproof_shielded_converts; // [u8; GROTH_PROOF_SIZE] where GROTH_PROOF_SSIZE = 48 + 96 + 48 = 192 + bytes_t zkproof_shielded_outputs; // [u8; GROTH_PROOF_SIZE] where GROTH_PROOF_SSIZE = 48 + 96 + 48 = 192 + + bytes_t authorization; // 64 bytes: rbar: [u8; 32], sbar: [u8; 32], } masp_sapling_bundle_t; - typedef struct{ bytes_t asset_type_id; // [u8;32] uint8_t has_asset_type_nonce; - uint8_t asset_type_nonce; // 1 byte int64_t value; // 8 bytes bytes_t transparent_address; // [u8;20] //bytes_t transparent_sig; // this seems to always be empty } masp_vin_t; +typedef struct{ + bytes_t asset_type_id; // [u8;32] + uint8_t has_asset_type_nonce; + int64_t value; // 8 bytes + bytes_t transparent_address; // [u8;20] + //bytes_t transparent_sig; // this seems to always be empty +} masp_vout_t; + // https://github.com/anoma/masp/blob/0d7dc07d24b878e9162c25260ed744265dd2f748/masp_primitives/src/transaction/components/transparent.rs#L32 typedef struct { - bytes_t vin; - bytes_t vout; + masp_vin_t vin; + masp_vout_t vout; bytes_t authorization; // nothing if Auth; for unauth a Vec } masp_transparent_bundle_t; + // For masp TxData definition, see: // https://github.com/anoma/masp/blob/0d7dc07d24b878e9162c25260ed744265dd2f748/masp_primitives/src/transaction.rs#L189-L190 typedef struct { @@ -117,9 +143,9 @@ typedef struct { uint32_t consensus_branch_id; // this is an enum with at the moment only 0 -> MASP uint32_t lock_time; uint32_t expiry_height; - uint8_t has_transparent_bundle; + uint16_t has_transparent_bundle; masp_transparent_bundle_t transparent_bundle; - uint8_t has_sapling_bundle; + uint16_t has_sapling_bundle; masp_sapling_bundle_t sapling_bundle; } masp_tx_data_t; @@ -127,7 +153,6 @@ typedef struct { bytes_t tx_id; // [u8;32] masp_tx_data_t data; } masp_tx_section_t; -#endif typedef struct { bytes_t extBytes; @@ -158,11 +183,9 @@ typedef struct { section_t data; section_t extraData[MAX_EXTRA_DATA_SECS]; signature_section_t signatures[MAX_SIGNATURE_SECS]; -#if(0) section_t ciphertext; // todo: if we need to parse this in future, it will not be a section_t masp_tx_section_t maspTx; section_t maspBuilder; // todo: if we need to parse this in future, it will not be a section_t -#endif } sections_t; typedef struct {