Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Add missing functions for address and implement parameters from json for c-bindings 
Following functions should be added for `Address`:

- `ergo_lib_address_to_ergo_tree`
- `ergo_lib_address_from_public_key`

To get current blockchain parameters which can be adjusted by miners through voting it would be useful to have an `ergo_lib_parameters_from_json` function exposed.


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index ee7636859..f22783d42 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Bindings:
 - [ergo-lib-ios(Swift)](https://github.com/ergoplatform/sigma-rust/tree/develop/bindings/ergo-lib-ios)
 - [ergo-lib-jni(Java)](https://github.com/ergoplatform/sigma-rust/tree/develop/bindings/ergo-lib-jni) [![Latest Version](https://img.shields.io/crates/v/ergo-lib-jni.svg)](https://crates.io/crates/ergo-lib-jni) [![Documentation](https://docs.rs/ergo-lib-jni/badge.svg)](https://docs.rs/crate/ergo-lib-jni)
 - [ergo-lib-c (C)](https://github.com/ergoplatform/sigma-rust/tree/develop/bindings/ergo-lib-c) [![Latest Version](https://img.shields.io/crates/v/ergo-lib-c.svg)](https://crates.io/crates/ergo-lib-c) [![Documentation](https://docs.rs/ergo-lib-c/badge.svg)](https://docs.rs/crate/ergo-lib-c)
+- [ergo-lib-go (Go)](https://github.com/sigmaspace-io/ergo-lib-go) [![Go Reference](https://pkg.go.dev/badge/github.com/sigmaspace-io/ergo-lib-go.svg)](https://pkg.go.dev/github.com/sigmaspace-io/ergo-lib-go)
 - [sigma_rb(Ruby)](https://github.com/thedlop/sigma_rb)[![Gem Version](https://badge.fury.io/rb/sigma_rb.svg)](https://badge.fury.io/rb/sigma_rb)
 
 ## Changelog
diff --git a/bindings/ergo-lib-c-core/src/address.rs b/bindings/ergo-lib-c-core/src/address.rs
index 2b43e6afe..464ce9312 100644
--- a/bindings/ergo-lib-c-core/src/address.rs
+++ b/bindings/ergo-lib-c-core/src/address.rs
@@ -1,8 +1,13 @@
+use crate::ergo_tree::{ErgoTree, ErgoTreePtr};
 use crate::{
     error::*,
     util::{const_ptr_as_ref, mut_ptr_as_mut},
 };
+use ergo_lib::ergo_chain_types::EcPoint;
 use ergo_lib::ergotree_ir::chain::address as addr;
+use ergo_lib::ergotree_ir::ergo_tree::ErgoTree as InternalErgoTree;
+use ergo_lib::ergotree_ir::serialization::SigmaSerializable;
+use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
 
 /// Address wrapper
 pub struct Address(pub(crate) ergo_lib::ergotree_ir::chain::address::Address);
@@ -97,6 +102,33 @@ pub unsafe fn address_from_ergo_tree(
     }
 }
 
+/// Get ergo tree for address
+pub unsafe fn address_to_ergo_tree(
+    address_ptr: ConstAddressPtr,
+    ergo_tree_out: *mut ErgoTreePtr,
+) -> Result<(), Error> {
+    let address = const_ptr_as_ref(address_ptr, "address_ptr")?;
+    let ergo_tree_out = mut_ptr_as_mut(ergo_tree_out, "ergo_tree_out")?;
+    let ergo_tree: InternalErgoTree = address.0.script().map_err(Error::misc)?;
+    *ergo_tree_out = Box::into_raw(Box::new(ErgoTree(ergo_tree)));
+    Ok(())
+}
+
+/// Create address from public key bytes
+pub unsafe fn address_from_public_key(
+    bytes_ptr: *const u8,
+    len: usize,
+    address_out: *mut AddressPtr,
+) -> Result<(), Error> {
+    let address_out = mut_ptr_as_mut(address_out, "address_out")?;
+    let bytes = std::slice::from_raw_parts(bytes_ptr, len);
+    let address = EcPoint::sigma_parse_bytes(bytes)
+        .map(|point| ergo_lib::ergotree_ir::chain::address::Address::P2Pk(ProveDlog::new(point)))
+        .map_err(Error::misc)?;
+    *address_out = Box::into_raw(Box::new(Address(address)));
+    Ok(())
+}
+
 /// Drop the `Address`
 pub unsafe fn address_delete(address: AddressPtr) {
     if !address.is_null() {
diff --git a/bindings/ergo-lib-c-core/src/parameters.rs b/bindings/ergo-lib-c-core/src/parameters.rs
index a2d011206..23bd19fd3 100644
--- a/bindings/ergo-lib-c-core/src/parameters.rs
+++ b/bindings/ergo-lib-c-core/src/parameters.rs
@@ -1,4 +1,7 @@
 //! Ergo blockchain state (for ErgoTree evaluation)
+
+use crate::util::mut_ptr_as_mut;
+use crate::Error;
 use ergo_lib::chain;
 
 /// Blockchain parameters
@@ -14,6 +17,47 @@ pub unsafe fn parameters_default(parameters_out: *mut ParametersPtr) {
     )));
 }
 
+/// Create new parameters from provided blockchain parameters
+#[allow(clippy::too_many_arguments)]
+pub unsafe fn parameters_new(
+    block_version: i32,
+    storage_fee_factor: i32,
+    min_value_per_byte: i32,
+    max_block_size: i32,
+    max_block_cost: i32,
+    token_access_cost: i32,
+    input_cost: i32,
+    data_input_cost: i32,
+    output_cost: i32,
+    parameters_out: *mut ParametersPtr,
+) -> Result<(), Error> {
+    let parameters_out = mut_ptr_as_mut(parameters_out, "parameters_out")?;
+    let parameters = chain::parameters::Parameters::new(
+        block_version,
+        storage_fee_factor,
+        min_value_per_byte,
+        max_block_size,
+        max_block_cost,
+        token_access_cost,
+        input_cost,
+        data_input_cost,
+        output_cost,
+    );
+    *parameters_out = Box::into_raw(Box::new(Parameters(parameters)));
+    Ok(())
+}
+
+/// Parse parameters from JSON. Supports Ergo Node API/Explorer API
+pub unsafe fn parameters_from_json(
+    json: &str,
+    parameters_out: *mut ParametersPtr,
+) -> Result<(), Error> {
+    let parameters_out = mut_ptr_as_mut(parameters_out, "parameters_out")?;
+    let parameters = serde_json::from_str(json).map(Parameters)?;
+    *parameters_out = Box::into_raw(Box::new(parameters));
+    Ok(())
+}
+
 pub unsafe fn parameters_delete(parameters: ParametersPtr) {
     if !parameters.is_null() {
         let boxed = Box::from_raw(parameters);
diff --git a/bindings/ergo-lib-c/src/address.rs b/bindings/ergo-lib-c/src/address.rs
index 84a0ad24d..9828b90b0 100644
--- a/bindings/ergo-lib-c/src/address.rs
+++ b/bindings/ergo-lib-c/src/address.rs
@@ -1,3 +1,5 @@
+use ergo_lib_c_core::address::{address_from_public_key, address_to_ergo_tree};
+use ergo_lib_c_core::ergo_tree::ErgoTreePtr;
 use ergo_lib_c_core::{
     address::{
         address_delete, address_from_base58, address_from_ergo_tree, address_from_mainnet,
@@ -66,6 +68,25 @@ pub unsafe extern "C" fn ergo_lib_address_from_ergo_tree(
     Error::c_api_from(res)
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn ergo_lib_address_to_ergo_tree(
+    address: ConstAddressPtr,
+    ergo_tree_out: *mut ErgoTreePtr,
+) {
+    #[allow(clippy::unwrap_used)]
+    address_to_ergo_tree(address, ergo_tree_out).unwrap()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn ergo_lib_address_from_public_key(
+    bytes_ptr: *const u8,
+    len: usize,
+    address_out: *mut AddressPtr,
+) -> ErrorPtr {
+    let res = address_from_public_key(bytes_ptr, len, address_out);
+    Error::c_api_from(res)
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn ergo_lib_address_type_prefix(address: ConstAddressPtr) -> u8 {
     #[allow(clippy::unwrap_used)]
diff --git a/bindings/ergo-lib-c/src/parameters.rs b/bindings/ergo-lib-c/src/parameters.rs
index 1e55e659f..042ccefdf 100644
--- a/bindings/ergo-lib-c/src/parameters.rs
+++ b/bindings/ergo-lib-c/src/parameters.rs
@@ -1,6 +1,11 @@
-use ergo_lib_c_core::parameters::{parameters_default, ParametersPtr};
+use ergo_lib_c_core::parameters::{
+    parameters_default, parameters_from_json, parameters_new, ParametersPtr,
+};
+use ergo_lib_c_core::Error;
+use std::ffi::CStr;
+use std::os::raw::c_char;
 
-use crate::delete_ptr;
+use crate::{delete_ptr, ErrorPtr};
 
 /// Return default blockchain parameters that were set at genesis
 #[no_mangle]
@@ -8,6 +13,47 @@ pub unsafe extern "C" fn ergo_lib_parameters_default(parameters_out: *mut Parame
     parameters_default(parameters_out);
 }
 
+/// Parse parameters from JSON. Supports Ergo Node API/Explorer API
+#[no_mangle]
+pub unsafe extern "C" fn ergo_lib_parameters_from_json(
+    json_str: *const c_char,
+    parameters_out: *mut ParametersPtr,
+) -> ErrorPtr {
+    let json = CStr::from_ptr(json_str).to_string_lossy();
+    let res = parameters_from_json(&json, parameters_out);
+    Error::c_api_from(res)
+}
+
+/// Create new parameters from provided blockchain parameters
+#[no_mangle]
+pub unsafe extern "C" fn ergo_lib_parameters_new(
+    block_version: i32,
+    storage_fee_factor: i32,
+    min_value_per_byte: i32,
+    max_block_size: i32,
+    max_block_cost: i32,
+    token_access_cost: i32,
+    input_cost: i32,
+    data_input_cost: i32,
+    output_cost: i32,
+    parameters_out: *mut ParametersPtr,
+) {
+    #[allow(clippy::unwrap_used)]
+    parameters_new(
+        block_version,
+        storage_fee_factor,
+        min_value_per_byte,
+        max_block_size,
+        max_block_cost,
+        token_access_cost,
+        input_cost,
+        data_input_cost,
+        output_cost,
+        parameters_out,
+    )
+    .unwrap();
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn ergo_lib_parameters_delete(parameters: ParametersPtr) {
     delete_ptr(parameters)
diff --git a/ergo-lib/src/chain/json.rs b/ergo-lib/src/chain/json.rs
index cdb7b11f1..211832f14 100644
--- a/ergo-lib/src/chain/json.rs
+++ b/ergo-lib/src/chain/json.rs
@@ -6,6 +6,7 @@ use ergotree_interpreter::sigma_protocol::prover::ProofBytes;
 
 pub(crate) mod context_extension;
 pub(crate) mod hint;
+pub(crate) mod parameters;
 pub(crate) mod transaction;
 
 /// Serde remote type
diff --git a/ergo-lib/src/chain/json/parameters.rs b/ergo-lib/src/chain/json/parameters.rs
new file mode 100644
index 000000000..26f4d561d
--- /dev/null
+++ b/ergo-lib/src/chain/json/parameters.rs
@@ -0,0 +1,75 @@
+use crate::chain::parameters::{Parameter, Parameters};
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
+pub struct ParametersJson {
+    #[cfg_attr(feature = "json", serde(rename = "blockVersion"))]
+    pub block_version: i32,
+    #[cfg_attr(feature = "json", serde(rename = "storageFeeFactor"))]
+    pub storage_fee_factor: i32,
+    #[cfg_attr(feature = "json", serde(rename = "minValuePerByte"))]
+    pub min_value_per_byte: i32,
+    #[cfg_attr(feature = "json", serde(rename = "maxBlockSize"))]
+    pub max_block_size: i32,
+    #[cfg_attr(feature = "json", serde(rename = "maxBlockCost"))]
+    pub max_block_cost: i32,
+    #[cfg_attr(feature = "json", serde(rename = "tokenAccessCost"))]
+    pub token_access_cost: i32,
+    #[cfg_attr(feature = "json", serde(rename = "inputCost"))]
+    pub input_cost: i32,
+    #[cfg_attr(feature = "json", serde(rename = "dataInputCost"))]
+    pub data_input_cost: i32,
+    #[cfg_attr(feature = "json", serde(rename = "outputCost"))]
+    pub output_cost: i32,
+}
+
+impl From<ParametersJson> for Parameters {
+    fn from(v: ParametersJson) -> Self {
+        let mut parameters_table = HashMap::new();
+        parameters_table.insert(Parameter::StorageFeeFactor, v.storage_fee_factor);
+        parameters_table.insert(Parameter::MinValuePerByte, v.min_value_per_byte);
+        parameters_table.insert(Parameter::TokenAccessCost, v.token_access_cost);
+        parameters_table.insert(Parameter::InputCost, v.input_cost);
+        parameters_table.insert(Parameter::DataInputCost, v.data_input_cost);
+        parameters_table.insert(Parameter::OutputCost, v.output_cost);
+        parameters_table.insert(Parameter::MaxBlockSize, v.max_block_size);
+        parameters_table.insert(Parameter::BlockVersion, v.block_version);
+        parameters_table.insert(Parameter::MaxBlockCost, v.max_block_cost);
+        Self { parameters_table }
+    }
+}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used)]
+mod tests {
+    use crate::chain::parameters::Parameters;
+
+    #[test]
+    fn parse_parameters() {
+        let node_info_json = r#"
+           {
+               "outputCost": 194,
+               "tokenAccessCost": 100,
+               "maxBlockCost": 8001091,
+               "height": 1259520,
+               "maxBlockSize": 1271009,
+               "dataInputCost": 100,
+               "blockVersion": 3,
+               "inputCost": 2407,
+               "storageFeeFactor": 1250000,
+               "minValuePerByte": 360
+           }
+        "#;
+        let params: Parameters = serde_json::from_str(node_info_json).unwrap();
+        assert_eq!(params.output_cost(), 194);
+        assert_eq!(params.token_access_cost(), 100);
+        assert_eq!(params.max_block_cost(), 8001091);
+        assert_eq!(params.max_block_size(), 1271009);
+        assert_eq!(params.data_input_cost(), 100);
+        assert_eq!(params.block_version(), 3);
+        assert_eq!(params.input_cost(), 2407);
+        assert_eq!(params.storage_fee_factor(), 1250000);
+        assert_eq!(params.min_value_per_byte(), 360);
+    }
+}
diff --git a/ergo-lib/src/chain/parameters.rs b/ergo-lib/src/chain/parameters.rs
index c71c59915..67ecce489 100644
--- a/ergo-lib/src/chain/parameters.rs
+++ b/ergo-lib/src/chain/parameters.rs
@@ -27,9 +27,15 @@ pub enum Parameter {
 }
 
 /// System parameters which can be adjusted via soft-fork
+#[cfg_attr(feature = "json", derive(serde::Deserialize))]
+#[cfg_attr(
+    feature = "json",
+    serde(try_from = "crate::chain::json::parameters::ParametersJson")
+)]
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Parameters {
-    parameters_table: HashMap<Parameter, i32>,
+    /// table of adjustable system parameters
+    pub parameters_table: HashMap<Parameter, i32>,
 }
 
 impl Parameters {
@@ -69,6 +75,32 @@ impl Parameters {
     pub fn output_cost(&self) -> i32 {
         self.parameters_table[&Parameter::OutputCost]
     }
+
+    /// Create new parameters from provided blockchain parameters
+    #[allow(clippy::too_many_arguments)]
+    pub fn new(
+        block_version: i32,
+        storage_fee_factor: i32,
+        min_value_per_byte: i32,
+        max_block_size: i32,
+        max_block_cost: i32,
+        token_access_cost: i32,
+        input_cost: i32,
+        data_input_cost: i32,
+        output_cost: i32,
+    ) -> Self {
+        let mut parameters_table = HashMap::new();
+        parameters_table.insert(Parameter::BlockVersion, block_version);
+        parameters_table.insert(Parameter::StorageFeeFactor, storage_fee_factor);
+        parameters_table.insert(Parameter::MinValuePerByte, min_value_per_byte);
+        parameters_table.insert(Parameter::MaxBlockSize, max_block_size);
+        parameters_table.insert(Parameter::MaxBlockCost, max_block_cost);
+        parameters_table.insert(Parameter::TokenAccessCost, token_access_cost);
+        parameters_table.insert(Parameter::InputCost, input_cost);
+        parameters_table.insert(Parameter::DataInputCost, data_input_cost);
+        parameters_table.insert(Parameter::OutputCost, output_cost);
+        Self { parameters_table }
+    }
 }
 
 impl std::default::Default for Parameters {


Test patch: