diff --git a/bindings/ergo-lib-c-core/src/constant.rs b/bindings/ergo-lib-c-core/src/constant.rs index d51d45763..c8044244a 100644 --- a/bindings/ergo-lib-c-core/src/constant.rs +++ b/bindings/ergo-lib-c-core/src/constant.rs @@ -58,6 +58,20 @@ pub unsafe fn constant_value_to_dbg_str(constant_ptr: ConstConstantPtr) -> Resul Ok(s) } +/// Create from i16 value +pub unsafe fn constant_from_i16(value: i16, constant_out: *mut ConstantPtr) -> Result<(), Error> { + let constant_out = mut_ptr_as_mut(constant_out, "constant_out")?; + *constant_out = Box::into_raw(Box::new(Constant(value.into()))); + Ok(()) +} + +/// Extract i16 value, returning error if wrong type +pub unsafe fn constant_to_i16(constant_ptr: ConstConstantPtr) -> Result { + let constant = const_ptr_as_ref(constant_ptr, "constant_ptr")?; + let i = i16::try_extract_from(constant.0.clone())?; + Ok(i) +} + /// Create from i32 value pub unsafe fn constant_from_i32(value: i32, constant_out: *mut ConstantPtr) -> Result<(), Error> { let constant_out = mut_ptr_as_mut(constant_out, "constant_out")?; diff --git a/bindings/ergo-lib-c-core/src/context_extension.rs b/bindings/ergo-lib-c-core/src/context_extension.rs index d42651706..8ca65836b 100644 --- a/bindings/ergo-lib-c-core/src/context_extension.rs +++ b/bindings/ergo-lib-c-core/src/context_extension.rs @@ -1,3 +1,4 @@ +use crate::constant::{ConstConstantPtr, Constant, ConstantPtr}; use crate::{ util::{const_ptr_as_ref, mut_ptr_as_mut}, Error, @@ -40,4 +41,36 @@ pub unsafe fn context_extension_keys( Ok(()) } -// TODO: get method (needs Constant) +/// Get value for key or fail if key is missing +pub unsafe fn context_extension_get( + context_extension_ptr: ConstContextExtensionPtr, + key: u8, + constant_out: *mut ConstantPtr, +) -> Result { + let context_extension = const_ptr_as_ref(context_extension_ptr, "context_extension_ptr")?; + let constant_out = mut_ptr_as_mut(constant_out, "constant_out")?; + let constant = context_extension + .0 + .values + .get(&key) + .map(|c| Constant(c.clone())); + + if let Some(constant) = constant { + *constant_out = Box::into_raw(Box::new(constant)); + Ok(true) + } else { + Ok(false) + } +} + +/// Set the supplied pair in the ContextExtension +pub unsafe fn context_extension_set_pair( + constant_ptr: ConstConstantPtr, + key: u8, + context_extension_ptr: ContextExtensionPtr, +) -> Result<(), Error> { + let constant = const_ptr_as_ref(constant_ptr, "constant_ptr")?; + let context_extension = mut_ptr_as_mut(context_extension_ptr, "context_extension_ptr")?; + context_extension.0.values.insert(key, constant.0.clone()); + Ok(()) +} diff --git a/bindings/ergo-lib-c-core/src/ergo_box.rs b/bindings/ergo-lib-c-core/src/ergo_box.rs index 03dafa43d..0cd238f30 100644 --- a/bindings/ergo-lib-c-core/src/ergo_box.rs +++ b/bindings/ergo-lib-c-core/src/ergo_box.rs @@ -19,6 +19,7 @@ use std::convert::{TryFrom, TryInto}; use ergo_lib::ergotree_ir::chain::{self, ergo_box::NonMandatoryRegisters}; +use ergo_lib::ergotree_ir::serialization::SigmaSerializable; use crate::{ collections::{CollectionPtr, ConstCollectionPtr}, @@ -326,6 +327,13 @@ pub unsafe fn ergo_box_to_json_eip12(ergo_box_ptr: ConstErgoBoxPtr) -> Result Result { + let ergo_box = const_ptr_as_ref(ergo_box_ptr, "ergo_box_ptr")?; + let b = ergo_box.0.sigma_serialize_bytes()?.len(); + Ok(b) +} + /// Pair of for a box #[derive(PartialEq, Eq, Debug, Clone)] pub struct ErgoBoxAssetsData(pub(crate) ergo_lib::wallet::box_selector::ErgoBoxAssetsData); diff --git a/bindings/ergo-lib-c/src/constant.rs b/bindings/ergo-lib-c/src/constant.rs index 6dab35522..752519cf1 100644 --- a/bindings/ergo-lib-c/src/constant.rs +++ b/bindings/ergo-lib-c/src/constant.rs @@ -77,6 +77,30 @@ pub unsafe extern "C" fn ergo_lib_constant_value_to_dbg_str( Error::c_api_from(res) } +/// Create from i16 value +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_constant_from_i16(value: i16, constant_out: *mut ConstantPtr) { + #[allow(clippy::unwrap_used)] + constant_from_i16(value, constant_out).unwrap(); +} + +/// Extract i16 value, returning error if wrong type +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_constant_to_i16( + constant_ptr: ConstConstantPtr, +) -> ReturnNum { + match constant_to_i16(constant_ptr) { + Ok(value) => ReturnNum { + value, + error: std::ptr::null_mut(), + }, + Err(e) => ReturnNum { + value: 0, // Just a dummy value + error: Error::c_api_from(Err(e)), + }, + } +} + /// Create from i32 value #[no_mangle] pub unsafe extern "C" fn ergo_lib_constant_from_i32(value: i32, constant_out: *mut ConstantPtr) { diff --git a/bindings/ergo-lib-c/src/context_extension.rs b/bindings/ergo-lib-c/src/context_extension.rs index a43d6ec34..2d58ed7c7 100644 --- a/bindings/ergo-lib-c/src/context_extension.rs +++ b/bindings/ergo-lib-c/src/context_extension.rs @@ -1,6 +1,8 @@ +use ergo_lib_c_core::constant::{ConstConstantPtr, ConstantPtr}; use ergo_lib_c_core::context_extension::*; +use ergo_lib_c_core::Error; -use crate::delete_ptr; +use crate::{delete_ptr, ReturnOption}; /// Create new empty ContextExtension instance #[no_mangle] @@ -30,6 +32,38 @@ pub unsafe extern "C" fn ergo_lib_context_extension_keys( context_extension_keys(context_extension_ptr, output).unwrap(); } +/// Returns constant with given key +/// or None if key doesn't exist +/// or error if constants parsing were failed +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_context_extension_get( + context_extension_ptr: ConstContextExtensionPtr, + key: u8, + constant_out: *mut ConstantPtr, +) -> ReturnOption { + match context_extension_get(context_extension_ptr, key, constant_out) { + Ok(is_some) => ReturnOption { + is_some, + error: std::ptr::null_mut(), + }, + Err(e) => ReturnOption { + is_some: false, // Just a dummy value + error: Error::c_api_from(Err(e)), + }, + } +} + +/// Set the supplied pair in the ContextExtension +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_context_extension_set_pair( + constant_ptr: ConstConstantPtr, + key: u8, + context_extension_ptr: ContextExtensionPtr, +) { + #[allow(clippy::unwrap_used)] + context_extension_set_pair(constant_ptr, key, context_extension_ptr).unwrap() +} + /// Drop `ContextExtension` #[no_mangle] pub unsafe extern "C" fn ergo_lib_context_extension_delete(ptr: ContextExtensionPtr) { diff --git a/bindings/ergo-lib-c/src/ergo_box.rs b/bindings/ergo-lib-c/src/ergo_box.rs index fea4e5c53..2c7590080 100644 --- a/bindings/ergo-lib-c/src/ergo_box.rs +++ b/bindings/ergo-lib-c/src/ergo_box.rs @@ -341,6 +341,13 @@ pub unsafe extern "C" fn ergo_lib_ergo_box_to_json_eip12( Error::c_api_from(res) } +/// Calculate serialized box size(in bytes) +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ergo_box_bytes_size(ergo_box_ptr: ConstErgoBoxPtr) -> usize { + #[allow(clippy::unwrap_used)] + ergo_box_bytes_size(ergo_box_ptr).unwrap() +} + /// Drop `ErgoBox` #[no_mangle] pub unsafe extern "C" fn ergo_lib_ergo_box_delete(ptr: ErgoBoxPtr) { diff --git a/bindings/ergo-lib-c/src/lib.rs b/bindings/ergo-lib-c/src/lib.rs index 3d181ce37..aaec33ed2 100644 --- a/bindings/ergo-lib-c/src/lib.rs +++ b/bindings/ergo-lib-c/src/lib.rs @@ -147,6 +147,7 @@ pub unsafe fn delete_ptr(ptr: *mut T) { pub trait IntegerType {} impl IntegerType for u8 {} +impl IntegerType for i16 {} impl IntegerType for i32 {} impl IntegerType for u32 {} impl IntegerType for i64 {} diff --git a/ergo-lib/src/chain/transaction.rs b/ergo-lib/src/chain/transaction.rs index f933ded67..cbb4c2e15 100644 --- a/ergo-lib/src/chain/transaction.rs +++ b/ergo-lib/src/chain/transaction.rs @@ -359,7 +359,7 @@ pub fn verify_tx_input_proof( let ctx = Rc::new(make_context(state_context, tx_context, input_idx)?); let verifier = TestVerifier; // Try spending in storage rent, if any condition is not satisfied fallback to normal script validation - match try_spend_storage_rent(input, state_context, &ctx) { + match try_spend_storage_rent(input, &input_box, state_context, &ctx) { Some(()) => Ok(VerificationResult { result: true, cost: 0, diff --git a/ergo-lib/src/chain/transaction/storage_rent.rs b/ergo-lib/src/chain/transaction/storage_rent.rs index 38e4b18d2..8a7c469b6 100644 --- a/ergo-lib/src/chain/transaction/storage_rent.rs +++ b/ergo-lib/src/chain/transaction/storage_rent.rs @@ -1,4 +1,5 @@ use ergotree_interpreter::{eval::context::Context, sigma_protocol::prover::ProofBytes}; +use ergotree_ir::chain::ergo_box::ErgoBox; use ergotree_ir::{ chain::ergo_box::RegisterId, mir::constant::TryExtractInto, serialization::SigmaSerializable, }; @@ -14,7 +15,20 @@ pub const STORAGE_EXTENSION_INDEX: u8 = i8::MAX as u8; // Attempt to spend a box with storage rent. Returns None if any of the required conditions is not met pub(crate) fn try_spend_storage_rent( - input_box: &Input, + input: &Input, + input_box: &ErgoBox, + state_context: &ErgoStateContext, + context: &Context, +) -> Option<()> { + if matches!(input.spending_proof.proof, ProofBytes::Empty) { + return check_storage_rent_conditions(input_box, state_context, context); + } + None +} + +// Checks if storage rent conditions are met. Returns None if any of the required conditions is not met +pub(crate) fn check_storage_rent_conditions( + input_box: &ErgoBox, state_context: &ErgoStateContext, context: &Context, ) -> Option<()> { @@ -23,7 +37,6 @@ pub(crate) fn try_spend_storage_rent( .height .checked_sub(context.self_box.creation_height)? >= STORAGE_PERIOD - && matches!(input_box.spending_proof.proof, ProofBytes::Empty) { let output_idx: i16 = context .extension @@ -33,8 +46,8 @@ pub(crate) fn try_spend_storage_rent( .clone() .try_extract_into() .ok()?; - let output_candidate = context.outputs.get(output_idx as usize)?; + let output_candidate = context.outputs.get(output_idx as usize)?; let storage_fee = input_box.sigma_serialize_bytes().ok()?.len() as u64 * state_context.parameters.storage_fee_factor() as u64; // If the box's value is less than the required storage fee, the box can be spent without any further restrictions @@ -42,7 +55,7 @@ pub(crate) fn try_spend_storage_rent( return Some(()); } if output_candidate.creation_height != state_context.pre_header.height - || *output_candidate.value.as_u64() < context.self_box.value.as_u64() - storage_fee + || *output_candidate.value.as_u64() < (context.self_box.value.as_u64() - storage_fee) { return None; } diff --git a/ergo-lib/src/wallet/signing.rs b/ergo-lib/src/wallet/signing.rs index 7fb17711a..b7938f3db 100644 --- a/ergo-lib/src/wallet/signing.rs +++ b/ergo-lib/src/wallet/signing.rs @@ -14,12 +14,13 @@ use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean; use std::rc::Rc; use std::sync::Arc; +use crate::chain::transaction::storage_rent::check_storage_rent_conditions; use crate::wallet::multi_sig::TransactionHintsBag; use ergotree_interpreter::eval::context::Context; use ergotree_interpreter::eval::env::Env; -use ergotree_interpreter::sigma_protocol::prover::Prover; use ergotree_interpreter::sigma_protocol::prover::ProverError; use ergotree_interpreter::sigma_protocol::prover::ProverResult; +use ergotree_interpreter::sigma_protocol::prover::{ProofBytes, Prover}; use thiserror::Error; pub use super::tx_context::TransactionContext; @@ -202,16 +203,29 @@ pub fn sign_tx_input( if let Some(bag) = tx_hints { hints_bag = bag.all_hints_for_input(input_idx); } - prover - .prove( - &input_box.ergo_tree, - &Env::empty(), - ctx, - message_to_sign, - &hints_bag, - ) - .map(|proof| Input::new(unsigned_input.box_id, proof.into())) - .map_err(|e| TxSigningError::ProverError(e, input_idx)) + + match check_storage_rent_conditions(&input_box, state_context, &ctx.clone()) { + // if input is storage rent set ProofBytes to empty because no proof is needed + Some(()) => Ok(Input::new( + unsigned_input.box_id, + ProverResult { + proof: ProofBytes::Empty, + extension: ctx.extension.clone(), + } + .into(), + )), + // if input is not storage rent use prover + None => prover + .prove( + &input_box.ergo_tree, + &Env::empty(), + ctx.clone(), + message_to_sign, + &hints_bag, + ) + .map(|proof| Input::new(unsigned_input.box_id, proof.into())) + .map_err(|e| TxSigningError::ProverError(e, input_idx)), + } } #[cfg(test)]