diff --git a/packages/flutter_nekoton_bridge/ios/Classes/frb.h b/packages/flutter_nekoton_bridge/ios/Classes/frb.h index d56d85fa..1f8f129b 100644 --- a/packages/flutter_nekoton_bridge/ios/Classes/frb.h +++ b/packages/flutter_nekoton_bridge/ios/Classes/frb.h @@ -292,8 +292,9 @@ intptr_t init_frb_dart_api_dl(void *obj); void wire_verify_signature(int64_t port_, struct wire_uint_8_list *public_key, - struct wire_uint_8_list *data_hash, - struct wire_uint_8_list *signature); + struct wire_uint_8_list *data, + struct wire_uint_8_list *signature, + int32_t *signature_id); void wire_nt_generate_key(int64_t port_, struct wire_MnemonicType *account_type); @@ -372,12 +373,14 @@ void wire_get_boc_hash(int64_t port_, struct wire_uint_8_list *boc); void wire_pack_into_cell(int64_t port_, struct wire_uint_8_list *params, - struct wire_uint_8_list *tokens); + struct wire_uint_8_list *tokens, + struct wire_uint_8_list *version); void wire_unpack_from_cell(int64_t port_, struct wire_uint_8_list *params, struct wire_uint_8_list *boc, - bool allow_partial); + bool allow_partial, + struct wire_uint_8_list *version); void wire_pack_std_smc_addr(int64_t port_, struct wire_uint_8_list *addr, diff --git a/packages/flutter_nekoton_bridge/lib/nekoton/crypto/crypto_lib.dart b/packages/flutter_nekoton_bridge/lib/nekoton/crypto/crypto_lib.dart index 05a86c6e..f9b5f1d8 100644 --- a/packages/flutter_nekoton_bridge/lib/nekoton/crypto/crypto_lib.dart +++ b/packages/flutter_nekoton_bridge/lib/nekoton/crypto/crypto_lib.dart @@ -12,12 +12,14 @@ export 'password_cache/password_cache_lib.dart'; /// Check signature by publicKey and data hash Future verifySignature({ required PublicKey publicKey, - required String dataHash, + required String data, required String signature, + required int? signatureId, }) { return createLib().verifySignature( publicKey: publicKey.publicKey, - dataHash: dataHash, + data: data, signature: signature, + signatureId: signatureId, ); } diff --git a/packages/flutter_nekoton_bridge/lib/nekoton/helpers/abi.dart b/packages/flutter_nekoton_bridge/lib/nekoton/helpers/abi.dart index 15d369c9..e9936a48 100644 --- a/packages/flutter_nekoton_bridge/lib/nekoton/helpers/abi.dart +++ b/packages/flutter_nekoton_bridge/lib/nekoton/helpers/abi.dart @@ -206,10 +206,12 @@ Future getBocHash(String boc) { Future<(String, String)> packIntoCell({ required List params, required TokensObject tokens, + required String? abiVersion, }) async { final data = await createLib().packIntoCell( params: jsonEncode(params), tokens: jsonEncode(tokens), + version: abiVersion, ); return (data[0], data[1]); @@ -220,11 +222,13 @@ Future unpackFromCell({ required List params, required String boc, required bool allowPartial, + required String? abiVersion, }) async { return jsonDecode(await createLib().unpackFromCell( params: jsonEncode(params), boc: boc, allowPartial: allowPartial, + version: abiVersion, )); } diff --git a/packages/nekoton_bridge/lib/src/bridge_generated.dart b/packages/nekoton_bridge/lib/src/bridge_generated.dart index 64a7f34a..d1db5dd3 100644 --- a/packages/nekoton_bridge/lib/src/bridge_generated.dart +++ b/packages/nekoton_bridge/lib/src/bridge_generated.dart @@ -23,11 +23,12 @@ abstract class NekotonBridge { ///---------------------------- /// CONTENT OF src/nekoton_wrapper/crypto/crypto_api.rs ///---------------------------- - /// Check signature by publicKey and data hash + /// Check signature by publicKey and data Future verifySignature( {required String publicKey, - required String dataHash, + required String data, required String signature, + int? signatureId, dynamic hint}); FlutterRustBridgeTaskConstMeta get kVerifySignatureConstMeta; @@ -82,7 +83,7 @@ abstract class NekotonBridge { FlutterRustBridgeTaskConstMeta get kRunLocalConstMeta; /// Get address of tvc and contract_abi. - /// Returns list of [address, state_init, hash] or throws error + /// Returns list of [address, boc of state_init, hash] or throws error Future> getExpectedAddress( {required String tvc, required String contractAbi, @@ -186,7 +187,10 @@ abstract class NekotonBridge { /// Return base64 encoded bytes of tokens or throws error /// returns [tvc, hash] Future> packIntoCell( - {required String params, required String tokens, dynamic hint}); + {required String params, + required String tokens, + String? version, + dynamic hint}); FlutterRustBridgeTaskConstMeta get kPackIntoCellConstMeta; @@ -195,6 +199,7 @@ abstract class NekotonBridge { {required String params, required String boc, required bool allowPartial, + String? version, dynamic hint}); FlutterRustBridgeTaskConstMeta get kUnpackFromCellConstMeta; @@ -3681,18 +3686,20 @@ class NekotonBridgeImpl implements NekotonBridge { NekotonBridgeImpl.raw(this._platform); Future verifySignature( {required String publicKey, - required String dataHash, + required String data, required String signature, + int? signatureId, dynamic hint}) { var arg0 = _platform.api2wire_String(publicKey); - var arg1 = _platform.api2wire_String(dataHash); + var arg1 = _platform.api2wire_String(data); var arg2 = _platform.api2wire_String(signature); + var arg3 = _platform.api2wire_opt_box_autoadd_i32(signatureId); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => - _platform.inner.wire_verify_signature(port_, arg0, arg1, arg2), + _platform.inner.wire_verify_signature(port_, arg0, arg1, arg2, arg3), parseSuccessData: _wire2api_bool, constMeta: kVerifySignatureConstMeta, - argValues: [publicKey, dataHash, signature], + argValues: [publicKey, data, signature, signatureId], hint: hint, )); } @@ -3700,7 +3707,7 @@ class NekotonBridgeImpl implements NekotonBridge { FlutterRustBridgeTaskConstMeta get kVerifySignatureConstMeta => const FlutterRustBridgeTaskConstMeta( debugName: "verify_signature", - argNames: ["publicKey", "dataHash", "signature"], + argNames: ["publicKey", "data", "signature", "signatureId"], ); Future ntGenerateKey( @@ -4111,15 +4118,19 @@ class NekotonBridgeImpl implements NekotonBridge { ); Future> packIntoCell( - {required String params, required String tokens, dynamic hint}) { + {required String params, + required String tokens, + String? version, + dynamic hint}) { var arg0 = _platform.api2wire_String(params); var arg1 = _platform.api2wire_String(tokens); + var arg2 = _platform.api2wire_opt_String(version); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => - _platform.inner.wire_pack_into_cell(port_, arg0, arg1), + _platform.inner.wire_pack_into_cell(port_, arg0, arg1, arg2), parseSuccessData: _wire2api_StringList, constMeta: kPackIntoCellConstMeta, - argValues: [params, tokens], + argValues: [params, tokens, version], hint: hint, )); } @@ -4127,23 +4138,25 @@ class NekotonBridgeImpl implements NekotonBridge { FlutterRustBridgeTaskConstMeta get kPackIntoCellConstMeta => const FlutterRustBridgeTaskConstMeta( debugName: "pack_into_cell", - argNames: ["params", "tokens"], + argNames: ["params", "tokens", "version"], ); Future unpackFromCell( {required String params, required String boc, required bool allowPartial, + String? version, dynamic hint}) { var arg0 = _platform.api2wire_String(params); var arg1 = _platform.api2wire_String(boc); var arg2 = allowPartial; + var arg3 = _platform.api2wire_opt_String(version); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => - _platform.inner.wire_unpack_from_cell(port_, arg0, arg1, arg2), + _platform.inner.wire_unpack_from_cell(port_, arg0, arg1, arg2, arg3), parseSuccessData: _wire2api_String, constMeta: kUnpackFromCellConstMeta, - argValues: [params, boc, allowPartial], + argValues: [params, boc, allowPartial, version], hint: hint, )); } @@ -4151,7 +4164,7 @@ class NekotonBridgeImpl implements NekotonBridge { FlutterRustBridgeTaskConstMeta get kUnpackFromCellConstMeta => const FlutterRustBridgeTaskConstMeta( debugName: "unpack_from_cell", - argNames: ["params", "boc", "allowPartial"], + argNames: ["params", "boc", "allowPartial", "version"], ); Future packStdSmcAddr( diff --git a/packages/nekoton_bridge/lib/src/bridge_generated.io.dart b/packages/nekoton_bridge/lib/src/bridge_generated.io.dart index 55c23c3d..5bbd1c35 100644 --- a/packages/nekoton_bridge/lib/src/bridge_generated.io.dart +++ b/packages/nekoton_bridge/lib/src/bridge_generated.io.dart @@ -984,14 +984,16 @@ class NekotonBridgeWire implements FlutterRustBridgeWireBase { void wire_verify_signature( int port_, ffi.Pointer public_key, - ffi.Pointer data_hash, + ffi.Pointer data, ffi.Pointer signature, + ffi.Pointer signature_id, ) { return _wire_verify_signature( port_, public_key, - data_hash, + data, signature, + signature_id, ); } @@ -1001,10 +1003,15 @@ class NekotonBridgeWire implements FlutterRustBridgeWireBase { ffi.Int64, ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>('wire_verify_signature'); + ffi.Pointer, + ffi.Pointer)>>('wire_verify_signature'); late final _wire_verify_signature = _wire_verify_signaturePtr.asFunction< - void Function(int, ffi.Pointer, - ffi.Pointer, ffi.Pointer)>(); + void Function( + int, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); void wire_nt_generate_key( int port_, @@ -1430,33 +1437,40 @@ class NekotonBridgeWire implements FlutterRustBridgeWireBase { int port_, ffi.Pointer params, ffi.Pointer tokens, + ffi.Pointer version, ) { return _wire_pack_into_cell( port_, params, tokens, + version, ); } late final _wire_pack_into_cellPtr = _lookup< ffi.NativeFunction< - ffi.Void Function(ffi.Int64, ffi.Pointer, + ffi.Void Function( + ffi.Int64, + ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>('wire_pack_into_cell'); late final _wire_pack_into_cell = _wire_pack_into_cellPtr.asFunction< - void Function( - int, ffi.Pointer, ffi.Pointer)>(); + void Function(int, ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); void wire_unpack_from_cell( int port_, ffi.Pointer params, ffi.Pointer boc, bool allow_partial, + ffi.Pointer version, ) { return _wire_unpack_from_cell( port_, params, boc, allow_partial, + version, ); } @@ -1466,10 +1480,15 @@ class NekotonBridgeWire implements FlutterRustBridgeWireBase { ffi.Int64, ffi.Pointer, ffi.Pointer, - ffi.Bool)>>('wire_unpack_from_cell'); + ffi.Bool, + ffi.Pointer)>>('wire_unpack_from_cell'); late final _wire_unpack_from_cell = _wire_unpack_from_cellPtr.asFunction< - void Function(int, ffi.Pointer, - ffi.Pointer, bool)>(); + void Function( + int, + ffi.Pointer, + ffi.Pointer, + bool, + ffi.Pointer)>(); void wire_pack_std_smc_addr( int port_, diff --git a/packages/nekoton_bridge/lib/src/bridge_generated.web.dart b/packages/nekoton_bridge/lib/src/bridge_generated.web.dart index a1fd5ffd..79c53622 100644 --- a/packages/nekoton_bridge/lib/src/bridge_generated.web.dart +++ b/packages/nekoton_bridge/lib/src/bridge_generated.web.dart @@ -533,7 +533,7 @@ class NekotonBridgeWasmModule implements WasmModule { external Object /* Promise */ call([String? moduleName]); external NekotonBridgeWasmModule bind(dynamic thisArg, String moduleName); external dynamic /* void */ wire_verify_signature(NativePortType port_, - String public_key, String data_hash, String signature); + String public_key, String data, String signature, int? signature_id); external dynamic /* void */ wire_nt_generate_key( NativePortType port_, List account_type); @@ -607,10 +607,10 @@ class NekotonBridgeWasmModule implements WasmModule { NativePortType port_, String boc); external dynamic /* void */ wire_pack_into_cell( - NativePortType port_, String params, String tokens); + NativePortType port_, String params, String tokens, String? version); - external dynamic /* void */ wire_unpack_from_cell( - NativePortType port_, String params, String boc, bool allow_partial); + external dynamic /* void */ wire_unpack_from_cell(NativePortType port_, + String params, String boc, bool allow_partial, String? version); external dynamic /* void */ wire_pack_std_smc_addr( NativePortType port_, String addr, bool base64_url, bool bounceable); @@ -1386,8 +1386,9 @@ class NekotonBridgeWire : super(WasmModule.cast(module)); void wire_verify_signature(NativePortType port_, String public_key, - String data_hash, String signature) => - wasmModule.wire_verify_signature(port_, public_key, data_hash, signature); + String data, String signature, int? signature_id) => + wasmModule.wire_verify_signature( + port_, public_key, data, signature, signature_id); void wire_nt_generate_key(NativePortType port_, List account_type) => wasmModule.wire_nt_generate_key(port_, account_type); @@ -1473,13 +1474,14 @@ class NekotonBridgeWire void wire_get_boc_hash(NativePortType port_, String boc) => wasmModule.wire_get_boc_hash(port_, boc); - void wire_pack_into_cell( - NativePortType port_, String params, String tokens) => - wasmModule.wire_pack_into_cell(port_, params, tokens); + void wire_pack_into_cell(NativePortType port_, String params, String tokens, + String? version) => + wasmModule.wire_pack_into_cell(port_, params, tokens, version); void wire_unpack_from_cell(NativePortType port_, String params, String boc, - bool allow_partial) => - wasmModule.wire_unpack_from_cell(port_, params, boc, allow_partial); + bool allow_partial, String? version) => + wasmModule.wire_unpack_from_cell( + port_, params, boc, allow_partial, version); void wire_pack_std_smc_addr(NativePortType port_, String addr, bool base64_url, bool bounceable) => diff --git a/packages/nekoton_bridge/native/src/nekoton_wrapper/crypto/crypto_api.rs b/packages/nekoton_bridge/native/src/nekoton_wrapper/crypto/crypto_api.rs index de8e5f51..f379b13c 100644 --- a/packages/nekoton_bridge/native/src/nekoton_wrapper/crypto/crypto_api.rs +++ b/packages/nekoton_bridge/native/src/nekoton_wrapper/crypto/crypto_api.rs @@ -1,47 +1,30 @@ #![allow(unused_variables, dead_code)] pub use crate::nekoton_wrapper::crypto::models::UnsignedMessageBoxTrait; -use crate::nekoton_wrapper::{parse_public_key, HandleError}; +use crate::nekoton_wrapper::{ + helpers::{parse_hex_or_base64_bytes, parse_signature}, + parse_public_key, HandleError, +}; use ed25519_dalek::Verifier; pub use flutter_rust_bridge::RustOpaque; pub use nekoton::crypto::UnsignedMessage; -use std::convert::TryFrom; use std::sync::Arc; -/// Check signature by publicKey and data hash +/// Check signature by publicKey and data pub fn verify_signature( public_key: String, - data_hash: String, + data: String, signature: String, + signature_id: Option, ) -> anyhow::Result { - let public_key = parse_public_key(public_key).handle_error()?; + let public_key = parse_public_key(public_key)?; - let data_hash = match hex::decode(&data_hash) { - Ok(data_hash) => data_hash, - Err(e) => match base64::decode(&data_hash) { - Ok(data_hash) => data_hash, - Err(e) => return Err(anyhow::Error::msg(e)), - }, - }; + let data = parse_hex_or_base64_bytes(data).handle_error()?; + let signature = parse_signature(signature)?; - if data_hash.len() != 32 { - return Err(anyhow::Error::msg("Invalid data hash. Expected 32 bytes")); - } - - let signature = match base64::decode(&signature) { - Ok(signature) => signature, - Err(e) => match hex::decode(&signature) { - Ok(signature) => signature, - Err(_) => return Err(anyhow::Error::msg(e)), - }, - }; - - let signature = match ed25519_dalek::Signature::try_from(signature.as_slice()) { - Ok(signature) => signature, - Err(_) => return Err(anyhow::Error::msg("Invalid signature. Expected 64 bytes")), - }; + let data = nekoton::crypto::extend_with_signature_id(&data, signature_id); - Ok(public_key.verify(&data_hash, &signature).is_ok()) + Ok(public_key.verify(data.as_ref(), &signature).is_ok()) } /// This struct creates only in rust side and describes UnsignedMessage diff --git a/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/abi_api.rs b/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/abi_api.rs index b35a4b39..e0b23daf 100644 --- a/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/abi_api.rs +++ b/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/abi_api.rs @@ -7,9 +7,9 @@ use crate::nekoton_wrapper::helpers::models::{ DecodedEvent, DecodedInput, DecodedOutput, DecodedTransaction, ExecutionOutput, }; use crate::nekoton_wrapper::helpers::{ - make_boc, make_full_contract_state, parse_account_stuff, parse_cell, parse_contract_abi, - parse_method_name, parse_params_list, parse_slice, serialize_into_boc, - serialize_into_boc_with_hash, serialize_state_init_data_key, + make_boc, make_boc_with_hash, make_full_contract_state, parse_account_stuff, parse_cell, + parse_contract_abi, parse_method_name, parse_optional_abi_version, parse_params_list, + parse_slice, serialize_into_boc, serialize_into_boc_with_hash, serialize_state_init_data_key, }; use crate::nekoton_wrapper::{parse_address, parse_public_key, HandleError}; use nekoton::core::models::{Expiration, ExpireAt, Transaction}; @@ -76,7 +76,7 @@ pub fn run_local( } /// Get address of tvc and contract_abi. -/// Returns list of [address, state_init, hash] or throws error +/// Returns list of [address, boc of state_init, hash] or throws error pub fn get_expected_address( tvc: String, contract_abi: String, @@ -120,9 +120,11 @@ pub fn get_expected_address( let cell = state_init.serialize().handle_error()?; let repr_hash = cell.repr_hash().to_hex_string(); - let mut result = vec![format!("{workchain_id}:{repr_hash}")]; - result.extend(serialize_into_boc_with_hash(&cell)?); - Ok(result) + Ok(vec![ + format!("{workchain_id}:{repr_hash}"), + make_boc(&cell)?, + repr_hash, + ]) } /// Returns base64-encoded body that was encoded or throws error @@ -464,14 +466,18 @@ pub fn get_boc_hash(boc: String) -> anyhow::Result { /// Return base64 encoded bytes of tokens or throws error /// returns [tvc, hash] -pub fn pack_into_cell(params: String, tokens: String) -> anyhow::Result> { +pub fn pack_into_cell( + params: String, + tokens: String, + version: Option, +) -> anyhow::Result> { let params = parse_params_list(params)?; let tokens = serde_json::from_str::(&tokens).handle_error()?; let tokens = nekoton_abi::parse_abi_tokens(¶ms, tokens).handle_error()?; - let version = ton_abi::contract::AbiVersion { major: 2, minor: 2 }; + let version = parse_optional_abi_version(version)?; let cell = nekoton_abi::pack_into_cell(&tokens, version).handle_error()?; - serialize_into_boc_with_hash(&cell) + make_boc_with_hash(cell) } /// Parse list of params and return json-encoded Tokens or throws error @@ -479,11 +485,12 @@ pub fn unpack_from_cell( params: String, boc: String, allow_partial: bool, + version: Option, ) -> anyhow::Result { let params = parse_params_list(params)?; let body = base64::decode(boc).handle_error()?; let cell = ton_types::deserialize_tree_of_cells(&mut body.as_slice()).handle_error()?; - let version = ton_abi::contract::AbiVersion { major: 2, minor: 2 }; + let version = parse_optional_abi_version(version)?; let tokens = nekoton_abi::unpack_from_cell(¶ms, SliceData::load_cell(cell)?, allow_partial, version) @@ -541,10 +548,9 @@ pub fn extract_public_key(boc: String) -> anyhow::Result { /// Convert code to base64 tvc string and return it or throw error /// returns [tvc, hash] pub fn code_to_tvc(code: String) -> anyhow::Result> { - let cell = base64::decode(code).handle_error()?; - - let cell = ton_types::deserialize_tree_of_cells(&mut cell.as_slice())?; - serialize_into_boc_with_hash(&cell) + let cell = parse_cell(code).handle_error()?; + let state_init = nekoton_abi::code_to_tvc(cell).handle_error()?; + serialize_into_boc_with_hash(&state_init) } /// Merge code and data to tvc base64 string and return it or throw error @@ -556,8 +562,7 @@ pub fn merge_tvc(code: String, data: String) -> anyhow::Result> { ..Default::default() }; - let cell = state_init.serialize().handle_error()?; - serialize_into_boc_with_hash(&cell) + serialize_into_boc_with_hash(&state_init) } /// Split base64 tvc string into data and code. @@ -590,7 +595,7 @@ pub fn split_tvc(tvc: String) -> anyhow::Result>> { /// returns [tvc, hash] pub fn set_code_salt(code: String, salt: String) -> anyhow::Result> { let cell = nekoton_abi::set_code_salt(parse_cell(code)?, parse_cell(salt)?)?; - serialize_into_boc_with_hash(&cell) + make_boc_with_hash(cell) } /// Get salt from code if possible and return base64-encoded salt or throw error diff --git a/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/mod.rs b/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/mod.rs index 50d15dd6..e08ca58c 100644 --- a/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/mod.rs +++ b/packages/nekoton_bridge/native/src/nekoton_wrapper/helpers/mod.rs @@ -5,9 +5,9 @@ use crate::nekoton_wrapper::transport::models::FullContractState; use crate::nekoton_wrapper::HandleError; use nekoton::transport::models::RawContractState; use nekoton_abi::MethodName; -use std::str::FromStr; +use std::{convert::TryFrom, str::FromStr}; use ton_block::{AccountStuff, Deserializable, MaybeDeserialize, Serializable}; -use ton_types::SliceData; +use ton_types::{Cell, SliceData}; pub mod abi_api; pub mod models; @@ -47,10 +47,10 @@ pub fn parse_account_stuff(boc: String) -> anyhow::Result anyhow::Result { +pub fn parse_cell(boc: String) -> anyhow::Result { let boc = boc.trim(); if boc.is_empty() { - Ok(ton_types::Cell::default()) + Ok(Cell::default()) } else { let body = base64::decode(boc).handle_error()?; ton_types::deserialize_tree_of_cells(&mut body.as_slice()).handle_error() @@ -208,16 +208,89 @@ pub fn parse_param_type(kind: &str) -> anyhow::Result anyhow::Result { +pub fn make_boc(data: &Cell) -> anyhow::Result { ton_types::serialize_toc(data) .handle_error() .map(base64::encode) } -/// Returns [tvc, hash] +/// Parse string abi version into struct +pub fn parse_abi_version(version: &str) -> anyhow::Result { + let version = ton_abi::contract::AbiVersion::parse(version).handle_error()?; + if version.is_supported() { + Ok(version) + } else { + Err("Unsupported ABI version").handle_error() + } +} + +/// Parse string abi version into struct or default version 2.2 +pub fn parse_optional_abi_version( + version: Option, +) -> anyhow::Result { + match version { + Some(version) => Ok(parse_abi_version(&version).handle_error()?), + None => Ok(ton_abi::contract::ABI_VERSION_2_2), + } +} + +/// Returns [tvc, hash] from cell +pub fn make_boc_with_hash(cell: Cell) -> anyhow::Result> { + Ok([make_boc(&cell)?, cell.repr_hash().to_hex_string()].to_vec()) +} + +/// Returns [tvc, hash] with serialization of data pub fn serialize_into_boc_with_hash(data: &dyn Serializable) -> anyhow::Result> { let cell = data.serialize().handle_error()?; - Ok([make_boc(&cell)?, cell.repr_hash().to_hex_string()].to_vec()) + make_boc_with_hash(cell) +} + +/// Parse hex or base64 if failed and return bytes +/// If string is empty, return empty list +pub fn parse_hex_or_base64_bytes(data: String) -> anyhow::Result> { + let data = data.trim(); + if data.is_empty() { + return Ok(Default::default()); + } + + match parse_hex_bytes(data) { + Ok(signature) => Ok(signature), + Err(e) => match base64::decode(data) { + Ok(signature) => Ok(signature), + Err(_) => Err(e), + }, + } +} + +/// Parse base64 or hex if failed and return bytes +/// If string is empty, return empty list +pub fn parse_base64_or_hex_bytes(data: String) -> Result, base64::DecodeError> { + let data = data.trim(); + if data.is_empty() { + return Ok(Default::default()); + } + + match base64::decode(data) { + Ok(signature) => Ok(signature), + Err(e) => match parse_hex_bytes(data) { + Ok(signature) => Ok(signature), + Err(_) => Err(e), + }, + } +} + +/// Parse hex and return decoded bytes +pub fn parse_hex_bytes(data: &str) -> anyhow::Result> { + hex::decode(data.strip_prefix("0x").unwrap_or(data)).handle_error() +} + +/// Parse signature from string returning decoded object +pub fn parse_signature(signature: String) -> anyhow::Result { + let signature = parse_base64_or_hex_bytes(signature).handle_error()?; + match ed25519_dalek::Signature::try_from(signature.as_slice()) { + Ok(signature) => Ok(signature), + Err(_) => Err("Invalid signature. Expected 64 bytes").handle_error(), + } } /// Returns tvc as base64 @@ -236,28 +309,29 @@ pub fn make_full_contract_state( ) -> anyhow::Result> { let full_contract_state = match raw_contract_state { RawContractState::Exists(state) => { - let boc = state - .account - .serialize() - .as_ref() - .map(ton_types::serialize_toc) - .handle_error()? - .map(base64::encode) - .handle_error()?; + let boc = serialize_into_boc(&state.account).handle_error()?; let is_deployed = matches!( &state.account.storage.state, ton_block::AccountState::AccountActive { state_init: _ } ); let account = state.account.clone(); - let code_hash = get_code_hash(&account).handle_error()?; + let code_hash = match &state.account.storage.state { + ton_block::AccountState::AccountActive { + state_init: + ton_block::StateInit { + code: Some(code), .. + }, + } => Some(code.repr_hash().to_hex_string()), + _ => None, + }; Some(FullContractState { balance: account.storage.balance.grams.as_u128().to_string(), gen_timings: state.timings, last_transaction_id: Some(state.last_transaction_id), is_deployed, - code_hash: Some(code_hash), + code_hash, boc, }) }