From f140e126c830d219c5346aa16763c680980da314 Mon Sep 17 00:00:00 2001 From: Saeed Dadkhah Date: Wed, 13 Mar 2024 14:27:19 +0330 Subject: [PATCH] Add contract compression. (#44) * Add contract compression. * Move things around * Update readme. --- README.md | 2 + build.rs | 12 +++++- src/contract/factory.rs | 15 +++++-- src/contract/mod.rs | 85 ++++++++++++++++++++++++++++++++++++++++ src/error.rs | 3 ++ tests/deploy_contract.rs | 13 +++--- 6 files changed, 121 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index db0cbe0..938cd78 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,8 @@ Ok(BalanceResponse { nonce: 138, balance: 899999994124734000000000 }) Contract address: ZilAddress("0xC50C93831F6eAB4e4F011076dca6e887288cc872") ``` +Instead of `deploy`, you can use `deploy_compressed` if you like to deploy a compressed version of the contract. + ### Getting contract states Our contract has `owner`, an immutable state, and `welcome_msg`, a mutable one. We can get these states by calling the corresponding functions: ```rust,ignore diff --git a/build.rs b/build.rs index 24c7f1a..243b9fe 100644 --- a/build.rs +++ b/build.rs @@ -202,7 +202,17 @@ impl {contract_name} {{ {contract_deployment_params_for_init} ]); - Ok(Self::new(factory.deploy_from_file(&std::path::PathBuf::from({contract_path:?}), init, None).await?)) + Ok(Self::new(factory.deploy_from_file(&std::path::PathBuf::from({contract_path:?}), init, None, false).await?)) + }} + + pub async fn deploy_compressed(client: Arc {contract_deployment_params}) -> Result {{ + let factory = ContractFactory::new(client.clone()); + let init = Init(vec![ + ScillaVariable::new("_scilla_version".to_string(), "Uint32".to_string(), "0".to_value()), + {contract_deployment_params_for_init} + ]); + + Ok(Self::new(factory.deploy_from_file(&std::path::PathBuf::from({contract_path:?}), init, None, true).await?)) }} pub fn address(&self) -> &ZilAddress {{ diff --git a/src/contract/factory.rs b/src/contract/factory.rs index c90bcd5..e9aaeeb 100644 --- a/src/contract/factory.rs +++ b/src/contract/factory.rs @@ -8,7 +8,7 @@ use crate::{ Error, }; -use super::{BaseContract, Init}; +use super::{compress_contract, BaseContract, Init}; pub struct Factory { client: Arc, @@ -32,6 +32,7 @@ impl Factory { /// * `overridden_params`: `overridden_params` is an optional parameter of type `TransactionParams`. It /// allows you to override the default transaction parameters when deploying the contract. If you don't /// want to override any parameters, you can pass `None` as the value for this parameter. + /// * `do_contract_compression`: Set it to true if you want your contract gets compressed before deployment. /// /// Returns: /// @@ -58,7 +59,7 @@ impl Factory { /// /// let factory = ContractFactory::new(provider.into()); /// let init = Init(vec![ScillaVariable::new_from_str("_scilla_version", "Uint32", "0")]); - /// let contract = factory.deploy_from_file(&PathBuf::from("./tests/contracts/Timestamp.scilla"), init, None).await?; + /// let contract = factory.deploy_from_file(&PathBuf::from("./tests/contracts/Timestamp.scilla"), init, None, false).await?; /// println!("addr: {:?}", contract); /// Ok(()) /// } @@ -68,8 +69,16 @@ impl Factory { path: &Path, init: Init, overridden_params: Option, + do_contract_compression: bool, ) -> Result, Error> { - let contract_code = std::fs::read_to_string(path)?; + let contract_code = { + let code = std::fs::read_to_string(path)?; + if do_contract_compression { + compress_contract(&code)? + } else { + code + } + }; self.deploy_str(contract_code, init, overridden_params).await } diff --git a/src/contract/mod.rs b/src/contract/mod.rs index be21604..969fe5b 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -111,6 +111,8 @@ async fn main() -> anyhow::Result<()> { } ``` +Instead of `deploy`, you can use `deploy_compressed` if you like to deploy a compressed version of the contract. + Alternatively, If the contract is already deployed and you have its address, it's possible to create a new instance of the target contract by calling `attach` function: ``` use std::sync::Arc; @@ -253,6 +255,7 @@ pub mod transition_call; use std::{ops::Deref, str::FromStr, sync::Arc}; pub use factory::Factory as ContractFactory; +use regex::Regex; pub use scilla_value::*; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value as JsonValue; @@ -351,4 +354,86 @@ impl BaseContract { } } +pub fn compress_contract(code: &str) -> Result { + let remove_comments_regex = Regex::new(r"\(\*.*?\*\)")?; + let replace_whitespace_regex = Regex::new(r"(?m)(^[ \t]*\r?\n)|([ \t]+$)")?; + let code = remove_comments_regex.replace_all(code, ""); + let code = replace_whitespace_regex.replace_all(&code, "").to_string(); + Ok(code) +} + +#[cfg(test)] +mod tests { + use crate::contract::compress_contract; + + #[test] + fn compression_1_works() { + let code = r#"(***************************************************) +(* The contract definition *) +(***************************************************) +contract HelloWorld +(owner: ByStr20)"#; + let compressed = compress_contract(code).unwrap(); + assert_eq!( + &compressed, + r#"contract HelloWorld +(owner: ByStr20)"# + ); + } + + #[test] + fn compression_2_works() { + let code = r#"(*something*)contract HelloWorld +(owner: ByStr20)"#; + let compressed = compress_contract(code).unwrap(); + assert_eq!( + &compressed, + r#"contract HelloWorld +(owner: ByStr20)"# + ); + } + + #[test] + fn compression_3_works() { + let code = r#"contract HelloWorld (* a dummy comment*) +(owner: ByStr20)"#; + let compressed = compress_contract(code).unwrap(); + assert_eq!( + &compressed, + r#"contract HelloWorld +(owner: ByStr20)"# + ); + } + + #[test] + fn compression_4_works() { + let code = r#"contract WithComment (*contract name*) +() +(*fields*) +field welcome_msg : String = "" (*welcome*) (*another comment*) "#; + let compressed = compress_contract(code).unwrap(); + assert_eq!( + &compressed, + r#"contract WithComment +() +field welcome_msg : String = """# + ); + } +} + +/* + + it("#4", async function () { + const code = `contract WithComment (*contract name*) +() +(*fields*) +field welcome_msg : String = "" (*welcome*) (*another comment*) `; + const compressed = compressContract(code); + expect(compressed).to.be.eq(`contract WithComment +() +field welcome_msg : String = ""`); + }); +}); + */ + include!(concat!(env!("OUT_DIR"), "/scilla_contracts.rs")); diff --git a/src/error.rs b/src/error.rs index 8a4e82d..5684c56 100644 --- a/src/error.rs +++ b/src/error.rs @@ -85,4 +85,7 @@ pub enum Error { #[error(transparent)] KeystoreError(#[from] eth_keystore::KeystoreError), + + #[error(transparent)] + RegexError(#[from] regex::Error), } diff --git a/tests/deploy_contract.rs b/tests/deploy_contract.rs index f61f49e..f42a860 100644 --- a/tests/deploy_contract.rs +++ b/tests/deploy_contract.rs @@ -31,7 +31,10 @@ async fn deploy_contract_without_constructor_parameter(ctx: &TestContext) -> Res let init = Init(vec![ScillaVariable::new_from_str("_scilla_version", "Uint32", "0")]); - let contract = factory.deploy_from_file(&ctx.timestamp_contract(), init, None).await.unwrap(); + let contract = factory + .deploy_from_file(&ctx.timestamp_contract(), init, None, false) + .await + .unwrap(); println!("addr: {:?}", contract); Ok(()) @@ -49,7 +52,7 @@ async fn deploy_contract_with_constructor_parameter(ctx: &TestContext) -> Result ]); let contract = factory - .deploy_from_file(&ctx.hello_world_contract(), init, None) + .deploy_from_file(&ctx.hello_world_contract(), init, None, false) .await .unwrap(); @@ -86,7 +89,7 @@ async fn call_a_param_less_transition(ctx: &TestContext) -> Result<()> { ]); let contract = factory - .deploy_from_file(&ctx.hello_world_contract(), init, None) + .deploy_from_file(&ctx.hello_world_contract(), init, None, false) .await .unwrap(); @@ -109,7 +112,7 @@ async fn call_transition_with_single_string_param(ctx: &TestContext) -> Result<( ]); let contract = factory - .deploy_from_file(&ctx.hello_world_contract(), init, None) + .deploy_from_file(&ctx.hello_world_contract(), init, None, false) .await .unwrap(); @@ -138,7 +141,7 @@ async fn call_a_param_less_transition_though_the_rust_binding(ctx: &TestContext) ]); let contract = factory - .deploy_from_file(&ctx.hello_world_contract(), init, None) + .deploy_from_file(&ctx.hello_world_contract(), init, None, false) .await .unwrap();