1
0
Fork 0
mirror of synced 2025-09-23 12:18:44 +00:00

Marlin contract verifier key and proof parsing

This commit is contained in:
Nirvan Tyagi 2022-02-21 15:36:14 -08:00
parent 36dfba355e
commit edd122b062
11 changed files with 720 additions and 6 deletions

View file

@ -10,6 +10,7 @@ members = [
"zokrates_abi",
"zokrates_test",
"zokrates_core_test",
"zokrates_solidity_test",
]
exclude = ["zokrates_js"]

View file

@ -63,7 +63,10 @@ sha2 = { version = "0.9.3", optional = true }
[dev-dependencies]
wasm-bindgen-test = "^0.3.0"
pretty_assertions = "0.6.1"
ethabi = "16.0.0"
primitive-types = { version = "0.10", features = ["rlp"] }
zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"}
zokrates_solidity_test = { path = "../zokrates_solidity_test"}
[build-dependencies]
cc = { version = "1.0", features = ["parallel"], optional = true }

View file

@ -16,6 +16,7 @@ use ark_poly_commit::{
};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use sha2::Sha256;
use rand_0_8::SeedableRng;
use zokrates_field::{ArkFieldExtensions, Field};
@ -31,7 +32,6 @@ const MINIMUM_CONSTRAINT_COUNT: usize = 2;
impl<T: Field + ArkFieldExtensions> UniversalBackend<T, marlin::Marlin> for Ark {
fn universal_setup(size: u32) -> Vec<u8> {
use rand_0_8::SeedableRng;
let rng = &mut rand_0_8::rngs::StdRng::from_entropy();
@ -131,8 +131,6 @@ impl<T: Field + ArkFieldExtensions> Backend<T, marlin::Marlin> for Ark {
) -> Proof<<marlin::Marlin as Scheme<T>>::ProofPoints> {
let computation = Computation::with_witness(program, witness);
use rand_0_8::SeedableRng;
let rng = &mut rand_0_8::rngs::StdRng::from_entropy();
let pk = IndexProverKey::<
@ -299,8 +297,6 @@ impl<T: Field + ArkFieldExtensions> Backend<T, marlin::Marlin> for Ark {
},
};
use rand_0_8::SeedableRng;
let rng = &mut rand_0_8::rngs::StdRng::from_entropy();
ArkMarlin::<

View file

@ -1,5 +1,6 @@
use crate::proof_system::scheme::{Scheme, UniversalScheme};
use crate::proof_system::{G1Affine, G2Affine};
use crate::proof_system::{G1Affine, G2Affine, NotBw6_761Field};
use crate::proof_system::solidity::{SolidityCompatibleScheme, SolidityCompatibleField, solidity_pairing_lib};
use serde::{Deserialize, Serialize};
use zokrates_field::Field;
@ -49,3 +50,288 @@ impl<T: Field> Scheme<T> for Marlin {
}
impl<T: Field> UniversalScheme<T> for Marlin {}
impl<T: SolidityCompatibleField + NotBw6_761Field> SolidityCompatibleScheme<T> for Marlin {
fn export_solidity_verifier(vk: <Marlin as Scheme<T>>::VerificationKey) -> String {
let (template, solidity_pairing_lib) =
(String::from(CONTRACT_TEMPLATE), solidity_pairing_lib(false));
// Replace public parameters in template
let src = template
.replace("<%vk_num_variables%>", &vk.num_variables.to_string())
.replace("<%vk_num_constraints%>", &vk.num_constraints.to_string())
.replace("<%vk_num_non_zero%>", &vk.num_non_zero.to_string())
.replace("<%vk_num_instance_variables%>", &vk.num_instance_variables.to_string())
.replace("<%vk_index_comms_length%>", &vk.index_comms.len().to_string())
.replace("<%vk_populate_index_comms%>", &{
let mut populate_index_comms = String::new();
for (i, (g, _)) in vk.index_comms.iter().enumerate() {
populate_index_comms.push_str(&format!("vk.index_comms[{}] = Pairing.G1Point({});", i, &g.to_string()));
if i < vk.index_comms.len() - 1 {
populate_index_comms.push_str("\n ");
}
}
populate_index_comms
})
.replace("<%vk_kzg_g%>", &vk.vk.g.to_string())
.replace("<%vk_kzg_gamma_g%>", &vk.vk.gamma_g.to_string())
.replace("<%vk_kzg_h%>", &vk.vk.h.to_string())
.replace("<%vk_kzg_beta_h%>", &vk.vk.beta_h.to_string())
.replace("<%vk_max_degree%>", &vk.max_degree.to_string())
.replace("<%vk_supported_degree%>", &vk.supported_degree.to_string())
.replace("<%vk_degree_bounds_length%>", &vk.degree_bounds_and_shift_powers.as_ref().unwrap().len().to_string())
.replace("<%vk_populate_degree_bounds%>", &{
let mut populate_degree_bounds = String::new();
for (i, (b, g)) in vk.degree_bounds_and_shift_powers.as_ref().unwrap().iter().enumerate() {
populate_degree_bounds.push_str(&format!("vk.degree_bounds[{}] = {};", i, &b.to_string()));
populate_degree_bounds.push_str("\n ");
populate_degree_bounds.push_str(&format!("vk.degree_shifted_powers[{}] = Pairing.G1Point({});", i, &g.to_string()));
if i < vk.degree_bounds_and_shift_powers.as_ref().unwrap().len() - 1 {
populate_degree_bounds.push_str("\n ");
}
}
populate_degree_bounds
});
format!(
"{}{}",
solidity_pairing_lib, src
)
}
}
const CONTRACT_TEMPLATE: &str = r#"
contract Verifier {
using Pairing for *;
struct KZGVerifierKey {
Pairing.G1Point g;
Pairing.G1Point gamma_g;
Pairing.G2Point h;
Pairing.G2Point beta_h;
}
struct VerifierKey {
// index_info
uint64 num_variables;
uint64 num_constraints;
uint64 num_non_zero;
uint64 num_instance_variables;
// index commitments
Pairing.G1Point[] index_comms;
// verifier key
KZGVerifierKey vk;
uint64 max_degree;
uint64 supported_degree;
uint64[] degree_bounds;
Pairing.G1Point[] degree_shifted_powers;
}
struct Proof {
Pairing.G1Point[] comms_1;
Pairing.G1Point[] comms_2;
Pairing.G1Point degree_bound_comms_2_g1;
Pairing.G1Point[] comms_3;
Pairing.G1Point degree_bound_comms_3_g2;
uint256[] evals;
Pairing.G1Point batch_lc_proof_1;
uint256 degree_bound_batch_lc_proof_1;
Pairing.G1Point batch_lc_proof_2;
}
function verifierKey() internal pure returns (VerifierKey memory vk) {
vk.num_variables = <%vk_num_variables%>;
vk.num_constraints = <%vk_num_constraints%>;
vk.num_non_zero = <%vk_num_non_zero%>;
vk.num_instance_variables = <%vk_num_instance_variables%>;
vk.index_comms = new Pairing.G1Point[](<%vk_index_comms_length%>);
<%vk_populate_index_comms%>
vk.vk.g = Pairing.G1Point(<%vk_kzg_g%>);
vk.vk.gamma_g = Pairing.G1Point(<%vk_kzg_gamma_g%>);
vk.vk.h = Pairing.G2Point(<%vk_kzg_h%>);
vk.vk.beta_h = Pairing.G2Point(<%vk_kzg_beta_h%>);
vk.max_degree = <%vk_max_degree%>;
vk.supported_degree = <%vk_supported_degree%>;
vk.degree_bounds = new uint64[](<%vk_degree_bounds_length%>);
vk.degree_shifted_powers = new Pairing.G1Point[](<%vk_degree_bounds_length%>);
<%vk_populate_degree_bounds%>
}
function verify(uint[] memory input, Proof memory proof) public view returns (uint) {
uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
VerifierKey memory vk = verifierKey();
for (uint i = 0; i < input.length; i++) {
require(input[i] < snark_scalar_field);
}
return 0;
}
}
"#;
#[cfg(test)]
mod tests {
use crate::flat_absy::{FlatParameter, FlatVariable};
use crate::ir::{Interpreter, Prog, QuadComb, Statement};
use crate::proof_system::{UniversalBackend, Backend, Proof, Fr};
use crate::proof_system::ark::{Ark, parse_fr};
use zokrates_field::ArkFieldExtensions;
use super::*;
use zokrates_field::{Bn128Field};
use zokrates_solidity_test::{
contract::Contract,
evm::Evm,
address::Address,
to_be_bytes,
};
use rand_0_8::{rngs::StdRng, SeedableRng};
use ethabi::{Token};
use primitive_types::U256;
/// Helper methods for parsing group structure
pub fn encode_g1_element(g: &G1Affine) -> Token {
Token::Tuple(vec![
Token::Uint(U256::from(&hex::decode(&g.0.trim_start_matches("0x")).unwrap()[..])),
Token::Uint(U256::from(&hex::decode(&g.1.trim_start_matches("0x")).unwrap()[..])),
])
}
pub fn encode_g2_element(g: &G2Affine) -> Token {
Token::Tuple(vec![
Token::FixedArray(vec![
Token::Uint(U256::from(&hex::decode(&g.0.0.trim_start_matches("0x")).unwrap()[..])),
Token::Uint(U256::from(&hex::decode(&g.0.1.trim_start_matches("0x")).unwrap()[..])),
]),
Token::FixedArray(vec![
Token::Uint(U256::from(&hex::decode(&g.1.0.trim_start_matches("0x")).unwrap()[..])),
Token::Uint(U256::from(&hex::decode(&g.1.1.trim_start_matches("0x")).unwrap()[..])),
]),
])
}
pub fn encode_fr_element(f: &Fr) -> Token {
Token::Uint(U256::from(&hex::decode(&f.trim_start_matches("0x")).unwrap()[..]))
}
fn encode_verify_input(proof: Proof<<Marlin as Scheme<Bn128Field>>::ProofPoints>,) -> Vec<Token> {
let input = Token::Array(proof.inputs.iter().map(|s| {
let bytes = hex::decode(s.trim_start_matches("0x")).unwrap();
debug_assert_eq!(bytes.len(), 32);
Token::Uint(U256::from(&bytes[..]))
}).collect::<Vec<_>>());
let comms_1_token = Token::Array(proof.proof.commitments[0].iter().map(|(c, _)|{
encode_g1_element(c)
}).collect::<Vec<_>>());
let comms_2_token = Token::Array(proof.proof.commitments[1].iter().map(|(c, _)|{
encode_g1_element(c)
}).collect::<Vec<_>>());
let degree_bound_comms_2_g1_token = encode_g1_element(proof.proof.commitments[1][1].1.as_ref().unwrap());
let comms_3_token = Token::Array(proof.proof.commitments[2].iter().map(|(c, _)|{
encode_g1_element(c)
}).collect::<Vec<_>>());
let degree_bound_comms_3_g2_token = encode_g1_element(proof.proof.commitments[2][0].1.as_ref().unwrap());
let evals_token = Token::Array(proof.proof.evaluations.into_iter().map(|f| {
encode_fr_element(&parse_fr::<Bn128Field>(&Bn128Field::into_ark(f)))
}).collect::<Vec<_>>());
let pc_lc_opening_1_token = encode_g1_element(&proof.proof.pc_proof_proof[0].0);
let degree_bound_pc_lc_opening_1_token = encode_fr_element(&parse_fr::<Bn128Field>(&Bn128Field::into_ark(proof.proof.pc_proof_proof[0].1.clone().unwrap())));
let pc_lc_opening_2_token = encode_g1_element(&proof.proof.pc_proof_proof[1].0);
let proof_tokens = vec![
comms_1_token,
comms_2_token,
degree_bound_comms_2_g1_token,
comms_3_token,
degree_bound_comms_3_g2_token,
evals_token,
pc_lc_opening_1_token,
degree_bound_pc_lc_opening_1_token,
pc_lc_opening_2_token,
];
vec![input, Token::Tuple(proof_tokens)]
}
#[test]
fn verify_solidity_bn128() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
return_count: 1,
statements: vec![
Statement::constraint(
QuadComb::from_linear_combinations(
FlatVariable::new(0).into(),
FlatVariable::new(0).into(),
),
FlatVariable::new(1),
),
Statement::constraint(FlatVariable::new(1), FlatVariable::public(0)),
],
};
let srs = <Ark as UniversalBackend<Bn128Field, Marlin>>::universal_setup(5);
let keypair =
<Ark as UniversalBackend<Bn128Field, Marlin>>::setup(srs, program.clone().into())
.unwrap();
let interpreter = Interpreter::default();
let witness = interpreter
.execute(program.clone(), &[Bn128Field::from(42)])
.unwrap();
let proof = <Ark as Backend<Bn128Field, Marlin>>::generate_proof(
program.clone(),
witness,
keypair.pk,
);
//let ans = <Ark as Backend<Bn128Field, Marlin>>::verify(keypair.vk, proof);
//assert!(ans);
let mut src = <Marlin as SolidityCompatibleScheme<Bn128Field>>::export_solidity_verifier(keypair.vk);
src = src.replace("\"", "\\\"");
let solc_config = r#"
{
"language": "Solidity",
"sources": {
"input.sol": { "content": "<%src%>" }
},
"settings": {
"optimizer": { "enabled": <%opt%> },
"outputSelection": {
"*": {
"*": [
"evm.bytecode.object", "abi"
],
"": [ "*" ] } }
}
}"#
.replace("<%opt%>", &true.to_string())
.replace("<%src%>", &src);
let contract = Contract::compile_from_config(&solc_config, "Verifier").unwrap();
// Setup EVM
let mut rng = StdRng::seed_from_u64(0u64);
let mut evm = Evm::new();
let deployer = Address::random(&mut rng);
evm.create_account(&deployer, 0);
// Deploy contract
let create_result = evm.deploy(contract.encode_create_contract_bytes(&[]).unwrap(), &deployer).unwrap();
let contract_addr = create_result.addr.clone();
println!("Contract deploy gas cost: {}", create_result.gas);
// Call verify function on contract
let result = evm.call(contract.encode_call_contract_bytes("verify", &encode_verify_input(proof)).unwrap(), &contract_addr, &deployer).unwrap();
//assert_eq!(&result.out, &to_be_bytes(&U256::from(1)));
println!("{:?}", result);
}
}

View file

@ -0,0 +1,19 @@
[package]
name = "zokrates_solidity_test"
version = "0.1.0"
authors = ["Nirvan Tyagi <nirvan.tyagi@gmail.com>"]
edition = "2018"
# Modeled after the testing pipeline of the Fe project: https://github.com/ethereum/fe/
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ethabi = "16.0.0"
primitive-types = { version = "0.10", features = ["rlp"] }
hex = { version = "0.4" }
bytes = { version = "1.1", default-features = false }
serde_json = { version = "1.0" }
rand = { version = "0.8" }
revm = { git = "https://github.com/bluealloy/revm", version = "1.2" }
solc = { git = "https://github.com/g-r-a-n-t/solc-rust", rev = "52d4146" }

View file

@ -0,0 +1,7 @@
# Installation
```bash
sudo apt install cmake libboost-all-dev
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc
```

View file

@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
contract SimpleStorage {
Var myVariable;
struct Var {
uint v;
}
function set(Var memory x) public {
myVariable = x;
}
function get() public view returns (uint) {
return myVariable.v;
}
}

View file

@ -0,0 +1,34 @@
use ethabi::token::Token;
use primitive_types::H160;
use rand::Rng;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Address(pub H160);
impl Address {
pub fn random<R: Rng>(rng: &mut R) -> Self {
Self(H160(rng.gen()))
}
pub fn as_token(&self) -> Token {
Token::Address(self.0)
}
}
impl AsRef<H160> for Address {
fn as_ref(&self) -> &H160 {
&self.0
}
}
impl From<H160> for Address {
fn from(hash: H160) -> Self {
Self(hash)
}
}
impl Into<Token> for Address {
fn into(self) -> Token {
self.as_token()
}
}

View file

@ -0,0 +1,147 @@
use ethabi::{Contract as ContractAbi, Token};
use serde_json::from_str;
use solc::compile;
use std::{fs::File, io::Read, path::Path};
use crate::{Error, EvmTestError};
#[derive(Clone)]
pub struct Contract {
pub binary: Vec<u8>,
pub abi: ContractAbi,
}
impl Contract {
pub fn new(binary: Vec<u8>, abi: ContractAbi) -> Self {
Self { binary, abi }
}
pub fn compile_from_solidity_file<P: AsRef<Path>>(
path: P,
contract_name: &str,
opt: bool,
) -> Result<Self, Error> {
// Load source file
let mut src_file = File::open(path)
.map_err(|_| Box::new(EvmTestError("src file open failed".to_string())))?;
let mut src = String::new();
src_file
.read_to_string(&mut src)
.map_err(|_| Box::new(EvmTestError("src file read failed".to_string())))?;
src = src.replace("\"", "\\\"");
Self::compile_from_src_string(&src, contract_name, opt)
}
pub fn compile_from_src_string(
src: &String,
contract_name: &str,
opt: bool,
) -> Result<Self, Error> {
// Compile source file using solc
// Configuration: https://docs.soliditylang.org/en/v0.8.10/using-the-compiler.html
// TODO: Change output selection to only compile 'input' file
let solc_config = r#"
{
"language": "Solidity",
"sources": { "input.sol": { "content": "{src}" } },
"settings": {
"optimizer": { "enabled": {opt} },
"outputSelection": {
"*": {
"*": [
"evm.bytecode.object", "abi"
],
"": [ "*" ] } }
}
}"#
.replace("{opt}", &opt.to_string())
.replace("{src}", &src);
Self::compile_from_config(&solc_config, contract_name)
}
pub fn compile_from_config(
config: &String,
contract_name: &str,
) -> Result<Self, Error> {
// Compile source file using solc
// Configuration: https://docs.soliditylang.org/en/v0.8.10/using-the-compiler.html
let out = from_str::<serde_json::Value>(&compile(config))
.map_err(|_| Box::new(EvmTestError("solc compile failed".to_string())))?;
if out["errors"].is_array() {
if out["errors"]
.as_array()
.unwrap()
.iter()
.any(|e| e["severity"] == "error")
{
return Err(Box::new(EvmTestError(format!(
"solc compiled with errors: {}",
out["errors"]
))));
}
}
let binary = {
let hex_code = out["contracts"]["input.sol"][contract_name]["evm"]["bytecode"]
["object"]
.to_string()
.replace("\"", "");
let binary = hex::decode(&hex_code)
.map_err(|_| Box::new(EvmTestError("decode hex binary failed".to_string())))?;
binary
};
let abi = {
if out["contracts"]["input.sol"][contract_name]["abi"] == "null" {
return Err(Box::new(EvmTestError(
"solc compiled with null abi".to_string(),
)));
}
let abi = ContractAbi::load(
out["contracts"]["input.sol"][contract_name]["abi"]
.to_string()
.as_bytes(),
)
.map_err(|_| Box::new(EvmTestError("ethabi failed loading abi".to_string())))?;
abi
};
Ok(Contract { binary, abi })
}
pub fn encode_create_contract_bytes(&self, init: &[Token]) -> Result<Vec<u8>, Error> {
match &self.abi.constructor {
Some(constructor) => {
let binary = constructor
.encode_input(self.binary.clone().into(), init)
.map_err(|_| {
Box::new(EvmTestError(
"abi constructor failed to encode inputs".to_string(),
))
})?;
Ok(binary.to_vec())
},
None => Ok(self.binary.clone()),
}
}
pub fn encode_call_contract_bytes(&self, fn_name: &str, input: &[Token]) -> Result<Vec<u8>, Error> {
match self.abi.functions.get(fn_name) {
Some(f) => {
//let c = f[0].inputs.iter().map(|p| p.kind.clone()).collect::<Vec<_>>();
//println!("{:?}", c);
let call_binary = f[0].encode_input(input)
.map_err(|_| {
Box::new(EvmTestError(
"abi function failed to encode inputs".to_string(),
))
})?;
Ok(call_binary.to_vec())
},
None => Err(Box::new(EvmTestError("abi does not include function".to_string()))),
}
}
}

View file

@ -0,0 +1,121 @@
use primitive_types::U256;
use revm::{AccountInfo, InMemoryDB, Log, Return, TransactOut, TransactTo, EVM};
use crate::{address::Address, Error, EvmTestError};
#[derive(Debug)]
pub struct CallResult {
pub op_out: Return,
pub out: Vec<u8>,
pub gas: u64,
pub log_out: Vec<Log>,
}
#[derive(Debug)]
pub struct CreateContractResult {
pub addr: Address,
pub gas: u64,
}
pub struct Evm {
vm: EVM<InMemoryDB>,
}
impl Evm {
pub fn new() -> Self {
let mut vm = revm::new();
vm.database(InMemoryDB::default());
Self { vm }
}
pub fn call(&mut self, input: Vec<u8>, addr: &Address, caller: &Address) -> Result<CallResult, Error> {
self.vm.env.tx.caller = caller.as_ref().clone();
self.vm.env.tx.transact_to = TransactTo::Call(addr.as_ref().clone());
self.vm.env.tx.data = input.into();
let (op_out, tx_out, gas, log_out) = self.vm.transact_commit();
let out = match tx_out {
TransactOut::Call(out) => Ok(out.to_vec()),
_ => Err(Box::new(EvmTestError("call contract function failed".to_string()))),
}?;
Ok(CallResult {
op_out,
out,
gas,
log_out,
})
}
pub fn deploy(
&mut self,
contract: Vec<u8>,
deployer: &Address,
) -> Result<CreateContractResult, Error> {
match self
.vm
.db()
.unwrap()
.cache()
.get_key_value(deployer.as_ref())
{
Some(_) => {
self.vm.env.tx.caller = deployer.as_ref().clone();
self.vm.env.tx.transact_to = TransactTo::create();
self.vm.env.tx.data = contract.into();
let (_, tx_out, gas, _) = self.vm.transact_commit();
let contract_address = match tx_out {
TransactOut::Create(_, Some(addr)) => Ok(Address(addr)),
_ => Err(Box::new(EvmTestError("create contract failed".to_string()))),
}?;
Ok(CreateContractResult {
addr: contract_address,
gas,
})
}
None => Err(Box::new(EvmTestError(
"deployer address not found".to_string(),
))),
}
}
pub fn create_account(&mut self, address: &Address, balance: impl Into<U256>) {
let acc = AccountInfo::from_balance(balance.into());
self.vm
.db()
.unwrap()
.insert_cache(address.as_ref().clone(), acc);
}
pub fn set_account_balance(
&mut self,
address: &Address,
balance: impl Into<U256>,
) -> Result<(), Error> {
let mut acc = self
.vm
.db()
.unwrap()
.cache()
.get(address.as_ref())
.ok_or(Box::new(EvmTestError(
"account address not found".to_string(),
)))?
.clone();
acc.balance = balance.into();
self.vm
.db()
.unwrap()
.insert_cache(address.as_ref().clone(), acc);
Ok(())
}
pub fn balance_of(&mut self, address: &Address) -> U256 {
match self.vm.db().unwrap().cache().get(address.as_ref()) {
Some(acc) => acc.balance,
None => 0.into(),
}
}
pub fn get_account(&mut self, address: &Address) -> Option<AccountInfo> {
self.vm.db().unwrap().cache().get(address.as_ref()).cloned()
}
}

View file

@ -0,0 +1,82 @@
use primitive_types::U256;
use std::{
error::Error as ErrorTrait,
fmt::{self, Debug},
};
pub mod address;
pub mod contract;
pub mod evm;
pub type Error = Box<dyn ErrorTrait>;
#[derive(Debug)]
pub struct EvmTestError(String);
impl ErrorTrait for EvmTestError {
fn source(self: &Self) -> Option<&(dyn ErrorTrait + 'static)> {
None
}
}
impl fmt::Display for EvmTestError {
fn fmt(self: &Self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub fn to_be_bytes(n: &U256) -> [u8; 32] {
let mut input_bytes: [u8; 32] = [0; 32];
n.to_big_endian(&mut input_bytes);
input_bytes
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
address::Address,
contract::Contract,
evm::{Evm},
};
use rand::{rngs::StdRng, SeedableRng};
use ethabi::Token;
#[test]
fn simple_storage_contract_test() {
let mut rng = StdRng::seed_from_u64(0u64);
// Compile contract
let contract_path = format!(
"{}/contracts/simple_storage.sol",
env!("CARGO_MANIFEST_DIR")
);
let contract =
Contract::compile_from_solidity_file(contract_path, "SimpleStorage", false).unwrap();
// Setup EVM
let mut evm = Evm::new();
let deployer = Address::random(&mut rng);
evm.create_account(&deployer, 0);
// Deploy contract
let create_result = evm.deploy(contract.encode_create_contract_bytes(&[]).unwrap(), &deployer).unwrap();
let contract_addr = create_result.addr.clone();
println!("Contract deploy gas cost: {}", create_result.gas);
// Call get function on contract
let get_result = evm.call(contract.encode_call_contract_bytes("get", &[]).unwrap(), &contract_addr, &deployer).unwrap();
assert_eq!(&get_result.out, &to_be_bytes(&U256::from(0)));
println!("{:?}", get_result);
// Call set function on contract
let set_result = evm.call(contract.encode_call_contract_bytes("set", &[Token::Tuple(vec![Token::Uint(U256::from(40))])]).unwrap(), &contract_addr, &deployer).unwrap();
println!("{:?}", set_result);
// Call get function on contract
let get_result = evm.call(contract.encode_call_contract_bytes("get", &[]).unwrap(), &contract_addr, &deployer).unwrap();
assert_eq!(&get_result.out, &to_be_bytes(&U256::from(40)));
println!("{:?}", get_result);
}
}