Skip to content

Commit 6777d65

Browse files
Address two issues with EVM (#3899)
## Motivation We identified two problems with the EVM, which we correct now. Also, some typos from the closed PR #3883 are put there. ## Proposal The `gas_limit` is added to the BlobkEnv variable. A more serious problem is the constructor argument for the contract being run in `execute_message`. It was incorrect when running a non-trivial constructor on another chain. The serialization was missing. ## Test Plan A non-trivial constructor that does not do anything, just making a check, was introduced to `evm_example_execute_message.sol`. It triggers the previous problem. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links <!-- Optional section for related PRs, related issues, and other references. If needed, please create issues to track future improvements and link them here. --> - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent e77d758 commit 6777d65

File tree

8 files changed

+76
-37
lines changed

8 files changed

+76
-37
lines changed

examples/counter-no-graphql/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
This example application implements a simple counter contract, it is initialized with an
44
unsigned integer that can be increased by the `increment` operation. In contrast with the
5-
counter application, it works without GraphQL
5+
counter application, it works without GraphQL.
66

77
## How It Works
88

9-
It is a very basic Linera application, which is initialized by a `u64` which can be incremented
9+
It is a simple Linera application, which is initialized by a `u64` which can be incremented
1010
by a `u64`.
1111

1212
For example, if the contract was initialized with 1, querying the contract would give us 1. Now if we want to

examples/counter/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ unsigned integer that can be increased by the `increment` operation.
55

66
## How It Works
77

8-
It is a very basic Linera application, which is initialized by a `u64` which can be incremented
8+
It is a simple Linera application, which is initialized by a `u64` which can be incremented
99
by a `u64`.
1010

1111
For example, if the contract was initialized with 1, querying the contract would give us 1. Now if we want to

linera-execution/src/evm/database.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{
99
sync::{Arc, Mutex},
1010
};
1111

12+
use linera_base::vm::VmRuntime;
1213
use linera_views::common::from_bytes_option;
1314
use revm::{
1415
db::AccountState,
@@ -17,7 +18,13 @@ use revm::{
1718
};
1819
use revm_primitives::{address, Address, BlobExcessGasAndPrice, BlockEnv, EvmState, B256, U256};
1920

20-
use crate::{BaseRuntime, Batch, ContractRuntime, ExecutionError, ViewError};
21+
use crate::{BaseRuntime, Batch, ContractRuntime, ExecutionError, ServiceRuntime, ViewError};
22+
23+
// The runtime costs are not available in service operations.
24+
// We need to set a limit to gas usage in order to avoid blocking
25+
// the validator.
26+
// We set up the limit similarly to Infura to 20 million.
27+
pub const EVM_SERVICE_GAS_LIMIT: u64 = 20_000_000;
2128

2229
/// The cost of loading from storage.
2330
const SLOAD_COST: u64 = 2100;
@@ -329,12 +336,7 @@ where
329336
let result = runtime.contains_keys_wait(&promise)?;
330337
Ok(result[0] && result[1])
331338
}
332-
}
333339

334-
impl<Runtime> DatabaseRuntime<Runtime>
335-
where
336-
Runtime: BaseRuntime,
337-
{
338340
pub fn get_block_env(&self) -> Result<BlockEnv, ExecutionError> {
339341
let mut runtime = self.runtime.lock().expect("The lock should be possible");
340342
// The block height being used
@@ -344,8 +346,7 @@ where
344346
let beneficiary = address!("00000000000000000000000000000000000000bb");
345347
// The difficulty which is no longer relevant after The Merge.
346348
let difficulty = U256::ZERO;
347-
// We do not have access to the Resources so we keep it to the maximum
348-
// and the control is done elsewhere.
349+
// Set up in the next section.
349350
let gas_limit = U256::MAX;
350351
// The timestamp. Both the EVM and Linera use the same UNIX epoch.
351352
// But the Linera epoch is in microseconds since the start and the
@@ -377,4 +378,35 @@ where
377378
blob_excess_gas_and_price,
378379
})
379380
}
381+
382+
pub fn constructor_argument(&self) -> Result<Vec<u8>, ExecutionError> {
383+
let mut runtime = self.runtime.lock().expect("The lock should be possible");
384+
let constructor_argument = runtime.application_parameters()?;
385+
Ok(serde_json::from_slice::<Vec<u8>>(&constructor_argument)?)
386+
}
387+
}
388+
389+
impl<Runtime> DatabaseRuntime<Runtime>
390+
where
391+
Runtime: ContractRuntime,
392+
{
393+
pub fn get_contract_block_env(&self) -> Result<BlockEnv, ExecutionError> {
394+
let mut block_env = self.get_block_env()?;
395+
let mut runtime = self.runtime.lock().expect("The lock should be possible");
396+
// We use the gas_limit from the runtime
397+
let gas_limit = runtime.maximum_fuel_per_block(VmRuntime::Evm)?;
398+
block_env.gas_limit = U256::from(gas_limit);
399+
Ok(block_env)
400+
}
401+
}
402+
403+
impl<Runtime> DatabaseRuntime<Runtime>
404+
where
405+
Runtime: ServiceRuntime,
406+
{
407+
pub fn get_service_block_env(&self) -> Result<BlockEnv, ExecutionError> {
408+
let mut block_env = self.get_block_env()?;
409+
block_env.gas_limit = U256::from(EVM_SERVICE_GAS_LIMIT);
410+
Ok(block_env)
411+
}
380412
}

linera-execution/src/evm/revm.rs

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use {
3434
};
3535

3636
use crate::{
37-
evm::database::{DatabaseRuntime, StorageStats},
37+
evm::database::{DatabaseRuntime, StorageStats, EVM_SERVICE_GAS_LIMIT},
3838
ContractRuntime, ContractSyncRuntimeHandle, EvmExecutionError, EvmRuntime, ExecutionError,
3939
ServiceRuntime, ServiceSyncRuntimeHandle, UserContract, UserContractInstance,
4040
UserContractModule, UserService, UserServiceInstance, UserServiceModule,
@@ -735,12 +735,8 @@ where
735735
/// Initializes the contract.
736736
fn initialize_contract(&mut self) -> Result<(), ExecutionError> {
737737
let mut vec_init = self.module.clone();
738-
let argument = {
739-
let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
740-
runtime.application_parameters()?
741-
};
742-
let argument = serde_json::from_slice::<Vec<u8>>(&argument)?;
743-
vec_init.extend_from_slice(&argument);
738+
let constructor_argument = self.db.constructor_argument()?;
739+
vec_init.extend_from_slice(&constructor_argument);
744740
let result = self.transact_commit(Choice::Create, &vec_init)?;
745741
self.write_logs(result.logs, "deploy")
746742
}
@@ -760,11 +756,11 @@ where
760756
let mut inspector = CallInterceptorContract {
761757
db: self.db.clone(),
762758
};
763-
let block_env = self.db.get_block_env()?;
759+
let block_env = self.db.get_contract_block_env()?;
764760
let gas_limit = {
765761
let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
766-
runtime.remaining_fuel(VmRuntime::Evm)
767-
}?;
762+
runtime.remaining_fuel(VmRuntime::Evm)?
763+
};
768764
let result = {
769765
let mut evm: Evm<'_, _, _> = Evm::builder()
770766
.with_ref_db(&mut self.db)
@@ -881,11 +877,8 @@ where
881877
if !self.db.is_initialized()? {
882878
let changes = {
883879
let mut vec_init = self.module.clone();
884-
let argument = {
885-
let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
886-
runtime.application_parameters()?
887-
};
888-
vec_init.extend_from_slice(&argument);
880+
let constructor_argument = self.db.constructor_argument()?;
881+
vec_init.extend_from_slice(&constructor_argument);
889882
let kind = TxKind::Create;
890883
let (_, changes) = self.transact(kind, &vec_init)?;
891884
changes
@@ -910,12 +903,7 @@ where
910903
db: self.db.clone(),
911904
};
912905

913-
let block_env = self.db.get_block_env()?;
914-
// The runtime costs are not available in service operations.
915-
// We need to set a limit to gas usage in order to avoid blocking
916-
// the validator.
917-
// We set up the limit similarly to Infura to 20 million.
918-
let gas_limit = 20_000_000;
906+
let block_env = self.db.get_service_block_env()?;
919907
let result_state = {
920908
let mut evm: Evm<'_, _, _> = Evm::builder()
921909
.with_ref_db(&mut self.db)
@@ -924,7 +912,7 @@ where
924912
tx.clear();
925913
tx.transact_to = kind;
926914
tx.data = tx_data;
927-
tx.gas_limit = gas_limit;
915+
tx.gas_limit = EVM_SERVICE_GAS_LIMIT;
928916
})
929917
.modify_block_env(|block| {
930918
*block = block_env;

linera-execution/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,9 @@ pub trait ContractRuntime: BaseRuntime {
720720
/// based on the execution context.
721721
fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
722722

723+
/// Returns the maximum gas fuel per block.
724+
fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
725+
723726
/// Returns the amount of execution fuel remaining before execution is aborted.
724727
fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
725728

linera-execution/src/runtime.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,14 @@ impl ContractRuntime for ContractSyncRuntimeHandle {
11741174
Ok(this.current_application().caller_id)
11751175
}
11761176

1177+
fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError> {
1178+
let policy = &self.inner().resource_controller.policy;
1179+
Ok(match vm_runtime {
1180+
VmRuntime::Wasm => policy.maximum_wasm_fuel_per_block,
1181+
VmRuntime::Evm => policy.maximum_evm_fuel_per_block,
1182+
})
1183+
}
1184+
11771185
fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError> {
11781186
Ok(self.inner().resource_controller.remaining_fuel(vm_runtime))
11791187
}

linera-service/tests/fixtures/evm_example_execute_message.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ contract ExampleExecuteMessage {
88
uint64 value;
99
Linera.MessageId last_message_id;
1010

11+
constructor(uint64 test_value) {
12+
require(test_value == 42);
13+
}
14+
1115
function instantiate(bytes memory input) external {
1216
uint64 read_value = abi.decode(input, (uint64));
1317
value = read_value;

linera-service/tests/linera_net_tests.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -744,11 +744,18 @@ async fn test_evm_execute_message_end_to_end_counter(config: impl LineraNetConfi
744744
// Creating the API of the contracts
745745

746746
sol! {
747+
struct ConstructorArgs {
748+
uint64 test_value;
749+
}
747750
function move_value_to_chain(bytes32 chain_id, uint64 moved_value);
748751
function get_value();
749752
}
753+
let query = get_valueCall {};
754+
let query = query.abi_encode();
755+
let query = EvmQuery::Query(query);
750756

751-
let constructor_argument = Vec::new();
757+
let constructor_argument = ConstructorArgs { test_value: 42 };
758+
let constructor_argument = constructor_argument.abi_encode();
752759

753760
let instantiation_argument: Vec<u8> = u64::abi_encode(&original_value);
754761

@@ -785,9 +792,6 @@ async fn test_evm_execute_message_end_to_end_counter(config: impl LineraNetConfi
785792
// Now checking the APIs.
786793
// First: checking the initial value of the contracts.
787794

788-
let query = get_valueCall {};
789-
let query = query.abi_encode();
790-
let query = EvmQuery::Query(query);
791795
let result = application1.run_json_query(query.clone()).await?;
792796
let counter_value = read_evm_u64_entry(result);
793797
assert_eq!(counter_value, original_value);

0 commit comments

Comments
 (0)