linera_execution/test_utils/
solidity.rs1use std::{
7 fs::File,
8 io::Write,
9 path::{Path, PathBuf},
10 process::{Command, Stdio},
11};
12
13use anyhow::Context;
14use revm_primitives::{Address, U256};
15use serde_json::Value;
16use tempfile::{tempdir, TempDir};
17
18use crate::{LINERA_SOL, LINERA_TYPES_SOL};
19
20fn write_compilation_json(path: &Path, file_name: &str) -> anyhow::Result<()> {
21 let mut source = File::create(path).unwrap();
22 writeln!(
23 source,
24 r#"
25{{
26 "language": "Solidity",
27 "sources": {{
28 "{file_name}": {{
29 "urls": ["./{file_name}"]
30 }}
31 }},
32 "settings": {{
33 "viaIR": true,
34 "outputSelection": {{
35 "*": {{
36 "*": ["evm.bytecode"]
37 }}
38 }}
39 }}
40}}
41"#
42 )?;
43 Ok(())
44}
45
46fn get_bytecode_path(path: &Path, file_name: &str, contract_name: &str) -> anyhow::Result<Vec<u8>> {
47 let config_path = path.join("config.json");
48 write_compilation_json(&config_path, file_name)?;
49 let config_file = File::open(config_path)?;
50
51 let output_path = path.join("result.json");
52 let output_file = File::create(output_path.clone())?;
53
54 let status = Command::new("solc")
55 .current_dir(path)
56 .arg("--standard-json")
57 .stdin(Stdio::from(config_file))
58 .stdout(Stdio::from(output_file))
59 .status()?;
60 assert!(status.success());
61
62 let contents = std::fs::read_to_string(output_path)?;
63 let json_data: serde_json::Value = serde_json::from_str(&contents)?;
64 let contracts = json_data
65 .get("contracts")
66 .with_context(|| format!("failed to get contracts in json_data={json_data}"))?;
67 let file_name_contract = contracts
68 .get(file_name)
69 .context("failed to get {file_name}")?;
70 let test_data = file_name_contract
71 .get(contract_name)
72 .with_context(|| format!("failed to get contract_name={contract_name}"))?;
73 let evm_data = test_data
74 .get("evm")
75 .with_context(|| format!("failed to get evm in test_data={test_data}"))?;
76 let bytecode = evm_data
77 .get("bytecode")
78 .with_context(|| format!("failed to get bytecode in evm_data={evm_data}"))?;
79 let object = bytecode
80 .get("object")
81 .with_context(|| format!("failed to get object in bytecode={bytecode}"))?;
82 let object = object.to_string();
83 let object = object.trim_matches(|c| c == '"').to_string();
84 Ok(hex::decode(&object)?)
85}
86
87pub fn get_bytecode(source_code: &str, contract_name: &str) -> anyhow::Result<Vec<u8>> {
88 let dir = tempdir().unwrap();
89 let path = dir.path();
90 if source_code.contains("Linera.sol") {
91 for (file_name, literal_path) in [
93 ("Linera.sol", LINERA_SOL),
94 ("LineraTypes.sol", LINERA_TYPES_SOL),
95 ] {
96 let test_code_path = path.join(file_name);
97 let mut test_code_file = File::create(&test_code_path)?;
98 writeln!(test_code_file, "{}", literal_path)?;
99 }
100 }
101 if source_code.contains("@openzeppelin") {
102 let _output = Command::new("npm")
103 .args(["install", "@openzeppelin/contracts"])
104 .current_dir(path)
105 .output()?;
106 let _output = Command::new("mv")
107 .args(["node_modules/@openzeppelin", "@openzeppelin"])
108 .current_dir(path)
109 .output()?;
110 }
111 let file_name = "test_code.sol";
112 let test_code_path = path.join(file_name);
113 let mut test_code_file = File::create(&test_code_path)?;
114 writeln!(test_code_file, "{}", source_code)?;
115 get_bytecode_path(path, file_name, contract_name)
116}
117
118pub fn load_solidity_example(path: &str) -> anyhow::Result<Vec<u8>> {
119 let source_code = std::fs::read_to_string(path)?;
120 let contract_name: &str = source_code
121 .lines()
122 .find_map(|line| line.trim_start().strip_prefix("contract "))
123 .ok_or_else(|| anyhow::anyhow!("Not matching"))?;
124 let contract_name: &str = contract_name
125 .split_whitespace()
126 .next()
127 .ok_or(anyhow::anyhow!("No space found after the contract name"))?;
128 tracing::info!("load_solidity_example, contract_name={contract_name}");
129 get_bytecode(&source_code, contract_name)
130}
131
132pub fn load_solidity_example_by_name(path: &str, contract_name: &str) -> anyhow::Result<Vec<u8>> {
133 let source_code = std::fs::read_to_string(path)?;
134 get_bytecode(&source_code, contract_name)
135}
136
137pub fn temporary_write_evm_module(module: Vec<u8>) -> anyhow::Result<(PathBuf, TempDir)> {
138 let dir = tempfile::tempdir()?;
139 let path = dir.path();
140 let app_file = "app.json";
141 let app_path = path.join(app_file);
142 {
143 std::fs::write(app_path.clone(), &module)?;
144 }
145 let evm_contract = app_path.to_path_buf();
146 Ok((evm_contract, dir))
147}
148
149pub fn get_evm_contract_path(path: &str) -> anyhow::Result<(PathBuf, TempDir)> {
150 let module = load_solidity_example(path)?;
151 temporary_write_evm_module(module)
152}
153
154pub fn value_to_vec_u8(value: Value) -> Vec<u8> {
155 let mut vec: Vec<u8> = Vec::new();
156 for val in value.as_array().unwrap() {
157 let val = val.as_u64().unwrap();
158 let val = val as u8;
159 vec.push(val);
160 }
161 vec
162}
163
164pub fn read_evm_u64_entry(value: Value) -> u64 {
165 let vec = value_to_vec_u8(value);
166 let mut arr = [0_u8; 8];
167 arr.copy_from_slice(&vec[24..]);
168 u64::from_be_bytes(arr)
169}
170
171pub fn read_evm_u256_entry(value: Value) -> U256 {
172 let result = value_to_vec_u8(value);
173 U256::from_be_slice(&result)
174}
175
176pub fn read_evm_address_entry(value: Value) -> Address {
177 let vec = value_to_vec_u8(value);
178 let mut arr = [0_u8; 20];
179 arr.copy_from_slice(&vec[12..]);
180 Address::from_slice(&arr)
181}