Skip to content

Commit affd869

Browse files
Allow EVM smart contract to create new EVM contracts. (#4273)
## Motivation It is very frequent for EVM contracts to create another contract. Thus, we need to support this feature. Fixes #3757 ## Proposal The implementation is surprisingly straightforward: * Create the blobs with `publish_module`. * Create the application with `create_application`. * Return the result according to the format of the EVM. The last step requires accessing the bytecode of a smart contract. This is implemented with a direct call. ## Test Plan In the CI, the test `test_evm_end_to_end_child_subcontract` has been added and shows the generation of the contract and access to them. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None.
1 parent 7924c03 commit affd869

File tree

7 files changed

+475
-86
lines changed

7 files changed

+475
-86
lines changed

linera-base/src/identifiers.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,18 @@ impl<A> ApplicationId<A> {
960960
}
961961
}
962962

963+
#[cfg(with_revm)]
964+
impl From<Address> for ApplicationId {
965+
fn from(address: Address) -> ApplicationId {
966+
let mut arr = [0_u8; 32];
967+
arr[..20].copy_from_slice(address.as_slice());
968+
ApplicationId {
969+
application_description_hash: arr.into(),
970+
_phantom: PhantomData,
971+
}
972+
}
973+
}
974+
963975
#[cfg(with_revm)]
964976
impl<A> ApplicationId<A> {
965977
/// Converts the `ApplicationId` into an Ethereum Address.

linera-execution/src/evm/database.rs

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ use revm_database::{AccountState, DBErrorMarker};
1818
use revm_primitives::{address, Address, B256, U256};
1919
use revm_state::{AccountInfo, Bytecode, EvmState};
2020

21-
use crate::{ApplicationId, BaseRuntime, Batch, ContractRuntime, ExecutionError, ServiceRuntime};
21+
use crate::{
22+
ApplicationId, BaseRuntime, Batch, ContractRuntime, EvmExecutionError, ExecutionError,
23+
ServiceRuntime,
24+
};
2225

2326
// The runtime costs are not available in service operations.
2427
// We need to set a limit to gas usage in order to avoid blocking
@@ -78,6 +81,10 @@ pub(crate) struct DatabaseRuntime<Runtime> {
7881
pub runtime: Arc<Mutex<Runtime>>,
7982
/// The uncommitted changes to the contract.
8083
pub changes: EvmState,
84+
/// Whether the contract has been instantiated in REVM.
85+
pub is_revm_instantiated: bool,
86+
/// The error that can occur during runtime.
87+
pub error: Arc<Mutex<Option<String>>>,
8188
}
8289

8390
impl<Runtime> Clone for DatabaseRuntime<Runtime> {
@@ -87,6 +94,8 @@ impl<Runtime> Clone for DatabaseRuntime<Runtime> {
8794
contract_address: self.contract_address,
8895
runtime: self.runtime.clone(),
8996
changes: self.changes.clone(),
97+
is_revm_instantiated: self.is_revm_instantiated,
98+
error: self.error.clone(),
9099
}
91100
}
92101
}
@@ -131,19 +140,33 @@ impl<Runtime: BaseRuntime> DatabaseRuntime<Runtime> {
131140
contract_address: Address::ZERO,
132141
runtime: Arc::new(Mutex::new(runtime)),
133142
changes: HashMap::new(),
143+
is_revm_instantiated: false,
144+
error: Arc::new(Mutex::new(None)),
134145
}
135146
}
136147

137148
/// Returns the current storage states and clears it to default.
138149
pub fn take_storage_stats(&self) -> StorageStats {
139-
let mut storage_stats_read = self
140-
.storage_stats
141-
.lock()
142-
.expect("The lock should be possible");
150+
let mut storage_stats_read = self.storage_stats.lock().unwrap();
143151
let storage_stats = storage_stats_read.clone();
144152
*storage_stats_read = StorageStats::default();
145153
storage_stats
146154
}
155+
156+
/// Insert error into the database
157+
pub fn insert_error(&self, exec_error: ExecutionError) {
158+
let mut error = self.error.lock().unwrap();
159+
*error = Some(format!("Runtime error {:?}", exec_error));
160+
}
161+
162+
/// Process the error.
163+
pub fn process_any_error(&self) -> Result<(), EvmExecutionError> {
164+
let error = self.error.lock().unwrap();
165+
if let Some(error) = error.clone() {
166+
return Err(EvmExecutionError::RuntimeError(error.clone()));
167+
}
168+
Ok(())
169+
}
147170
}
148171

149172
impl DBErrorMarker for ExecutionError {}
@@ -191,7 +214,7 @@ where
191214
let account = self.changes.get(&address).unwrap();
192215
return Ok(Some(account.info.clone()));
193216
}
194-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
217+
let mut runtime = self.runtime.lock().unwrap();
195218
let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, address);
196219
let promise = runtime.read_value_bytes_new(key_info)?;
197220
let result = runtime.read_value_bytes_wait(&promise)?;
@@ -214,14 +237,11 @@ where
214237
let key_prefix = self.get_address_key(KeyCategory::Storage as u8, address);
215238
let key = Self::get_linera_key(&key_prefix, index)?;
216239
{
217-
let mut storage_stats = self
218-
.storage_stats
219-
.lock()
220-
.expect("The lock should be possible");
240+
let mut storage_stats = self.storage_stats.lock().unwrap();
221241
storage_stats.key_read += 1;
222242
}
223243
let result = {
224-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
244+
let mut runtime = self.runtime.lock().unwrap();
225245
let promise = runtime.read_value_bytes_new(key)?;
226246
runtime.read_value_bytes_wait(&promise)
227247
}?;
@@ -239,11 +259,8 @@ where
239259
{
240260
/// Effectively commits changes to storage.
241261
pub fn commit_changes(&mut self) -> Result<(), ExecutionError> {
242-
let mut storage_stats = self
243-
.storage_stats
244-
.lock()
245-
.expect("The lock should be possible");
246-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
262+
let mut storage_stats = self.storage_stats.lock().unwrap();
263+
let mut runtime = self.runtime.lock().unwrap();
247264
let mut batch = Batch::new();
248265
for (address, account) in &self.changes {
249266
if !account.is_touched() {
@@ -305,35 +322,53 @@ where
305322
{
306323
/// Reads the nonce of the user
307324
pub fn get_nonce(&self, address: &Address) -> Result<u64, ExecutionError> {
308-
let account_info = self.basic_ref(*address)?;
325+
let account_info: Option<AccountInfo> = self.basic_ref(*address)?;
309326
Ok(match account_info {
310327
None => 0,
311328
Some(account_info) => account_info.nonce,
312329
})
313330
}
314331

332+
pub fn get_deployed_bytecode(&self) -> Result<Vec<u8>, ExecutionError> {
333+
let account_info = self.basic_ref(self.contract_address)?;
334+
Ok(match account_info {
335+
None => Vec::new(),
336+
Some(account_info) => {
337+
let bytecode = account_info
338+
.code
339+
.ok_or(EvmExecutionError::MissingBytecode)?;
340+
bytecode.bytes_ref().to_vec()
341+
}
342+
})
343+
}
344+
315345
/// Sets the EVM contract address from the value Address::ZERO.
316346
/// The value is set from the `ApplicationId`.
317347
pub fn set_contract_address(&mut self) -> Result<(), ExecutionError> {
318-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
348+
let mut runtime = self.runtime.lock().unwrap();
319349
let application_id = runtime.application_id()?;
320350
self.contract_address = application_id_to_address(application_id);
321351
Ok(())
322352
}
323353

324-
/// Checks if the contract is already initialized. It is possible
325-
/// that the constructor has not yet been called.
326-
pub fn is_initialized(&self) -> Result<bool, ExecutionError> {
327-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
354+
/// A contract is called initialized if the execution of the constructor
355+
/// with the constructor argument yield the storage and the deployed
356+
/// bytecode. The deployed bytecode is stored in the storage of the
357+
/// bytecode address.
358+
/// We determine whether the contract is already initialized, sets the
359+
/// `is_revm_initialized` and then returns the result.
360+
pub fn set_is_initialized(&mut self) -> Result<bool, ExecutionError> {
361+
let mut runtime = self.runtime.lock().unwrap();
328362
let evm_address = runtime.application_id()?.evm_address();
329363
let key_info = self.get_address_key(KeyCategory::AccountInfo as u8, evm_address);
330364
let promise = runtime.contains_key_new(key_info)?;
331365
let result = runtime.contains_key_wait(&promise)?;
366+
self.is_revm_instantiated = result;
332367
Ok(result)
333368
}
334369

335370
pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
336-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
371+
let mut runtime = self.runtime.lock().unwrap();
337372
// The block height being used
338373
let block_height_linera = runtime.block_height()?;
339374
let block_height_evm = block_height_linera.0;
@@ -376,7 +411,7 @@ where
376411
}
377412

378413
pub fn constructor_argument(&self) -> Result<Vec<u8>, ExecutionError> {
379-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
414+
let mut runtime = self.runtime.lock().unwrap();
380415
let constructor_argument = runtime.application_parameters()?;
381416
Ok(serde_json::from_slice::<Vec<u8>>(&constructor_argument)?)
382417
}
@@ -388,7 +423,7 @@ where
388423
{
389424
pub fn get_contract_block_env(&self) -> Result<BlockEnv, ExecutionError> {
390425
let mut block_env = self.get_block_env()?;
391-
let mut runtime = self.runtime.lock().expect("The lock should be possible");
426+
let mut runtime = self.runtime.lock().unwrap();
392427
// We use the gas_limit from the runtime
393428
let gas_limit = runtime.maximum_fuel_per_block(VmRuntime::Evm)?;
394429
block_env.gas_limit = gas_limit;

linera-execution/src/evm/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@ pub enum EvmExecutionError {
2525
CommitError(String),
2626
#[error("It is illegal to call {0} from an operation")]
2727
IllegalOperationCall(String),
28+
#[error("runtime error")]
29+
RuntimeError(String),
2830
#[error("The function {0} is being called but is missing from the bytecode API")]
2931
MissingFunction(String),
3032
#[error("Incorrect contract creation: {0}")]
3133
IncorrectContractCreation(String),
3234
#[error("The operation should contain the evm selector and so have length 4 or more")]
3335
OperationIsTooShort,
36+
#[error("Missing bytecode")]
37+
MissingBytecode,
3438
#[error("Transact error {0}")]
3539
TransactError(String),
40+
#[error("Impossible to create contracts in services")]
41+
NoContractCreationInService,
3642
#[error("Transact commit error {0}")]
3743
TransactCommitError(String),
3844
#[error("Precompile error: {0}")]

0 commit comments

Comments
 (0)