Skip to content

Commit 3580817

Browse files
Introduce the linera.sol for solidity code. (#3775)
## Motivation The introduction of the Wasm calls is making the code complicated. The `linera.sol` isolates the Linera specific code in a separate library. Fixes #3761 ## Proposal The following is done: * The `PrecompileTag` is introduced with only two options. This can be extended in the future. * The few lines in `evm_call_wasm_example_counter.sol` is moved to `linera.sol`. This again can be extended * The `linera.sol` is available as a fixture in the `linera-execution`. The compilation uses it if the solidity is selected. * The use of the function has a prefix "Linera." which makes it clear about what it is. ## Test Plan The CI. ## Release Plan Normal release mode. The objective is to have the EVM functional for the next TestNet. ## Links None
1 parent e916297 commit 3580817

File tree

9 files changed

+122
-44
lines changed

9 files changed

+122
-44
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ lru = "0.12.3"
123123
num-bigint = "0.4.3"
124124
num-format = "0.4.4"
125125
num-traits = "0.2.18"
126+
num_enum = "0.7.3"
126127
octocrab = "0.42.1"
127128
oneshot = "0.1.6"
128129
pathdiff = "0.2.1"

examples/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

linera-execution/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ linera-views.workspace = true
5252
linera-views-derive.workspace = true
5353
linera-witty = { workspace = true, features = ["log", "macros"] }
5454
lru.workspace = true
55+
num_enum.workspace = true
5556
oneshot.workspace = true
5657
prometheus = { workspace = true, optional = true }
5758
proptest = { workspace = true, optional = true }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
pragma solidity ^0.8.0;
4+
5+
// Precompile keys:
6+
// 0: try_call_application
7+
// 1: try_query_application
8+
9+
library Linera {
10+
function try_call_application(bytes32 universal_address, bytes memory operation) internal returns (bytes memory) {
11+
address precompile = address(0x0b);
12+
bytes1 input1 = bytes1(uint8(0));
13+
bytes memory input2 = abi.encodePacked(input1, universal_address, operation);
14+
(bool success, bytes memory output) = precompile.call(input2);
15+
require(success);
16+
return output;
17+
}
18+
19+
function try_query_application(bytes32 universal_address, bytes memory argument) internal returns (bytes memory) {
20+
address precompile = address(0x0b);
21+
bytes1 input1 = bytes1(uint8(1));
22+
bytes memory input2 = abi.encodePacked(input1, universal_address, argument);
23+
(bool success, bytes memory output) = precompile.call(input2);
24+
require(success);
25+
return output;
26+
}
27+
28+
}

linera-execution/src/evm/revm.rs

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! Code specific to the usage of the [Revm](https://bluealloy.github.io/revm/) runtime.
55
66
use core::ops::Range;
7-
use std::sync::Arc;
7+
use std::{convert::TryFrom, sync::Arc};
88

99
use alloy::primitives::Address;
1010
use linera_base::{
@@ -13,6 +13,7 @@ use linera_base::{
1313
identifiers::{ApplicationId, StreamName},
1414
vm::EvmQuery,
1515
};
16+
use num_enum::TryFromPrimitive;
1617
use revm::{
1718
db::WrapDatabaseRef, inspector_handle_register, primitives::Bytes, ContextPrecompile,
1819
ContextStatefulPrecompile, Evm, EvmContext, InnerEvmContext, Inspector,
@@ -238,6 +239,15 @@ fn address_to_user_application_id(address: Address) -> ApplicationId {
238239
u8_slice_to_application_id(&vec)
239240
}
240241

242+
#[repr(u8)]
243+
#[derive(TryFromPrimitive)]
244+
enum PrecompileTag {
245+
/// Key prefix for the try_call_application
246+
TryCallApplication,
247+
/// Key prefix for the try_query_application
248+
TryQueryApplication,
249+
}
250+
241251
struct GeneralContractCall;
242252

243253
impl<Runtime: ContractRuntime>
@@ -251,26 +261,38 @@ impl<Runtime: ContractRuntime>
251261
context: &mut InnerEvmContext<WrapDatabaseRef<&mut DatabaseRuntime<Runtime>>>,
252262
) -> PrecompileResult {
253263
let vec = input.to_vec();
254-
let target = u8_slice_to_application_id(&vec[0..32]);
255-
let argument: Vec<u8> = vec[32..].to_vec();
256-
let result = {
257-
let authenticated = true;
258-
let mut runtime = context
259-
.db
260-
.0
261-
.runtime
262-
.lock()
263-
.expect("The lock should be possible");
264-
runtime.try_call_application(authenticated, target, argument)
265-
}
266-
.map_err(|error| PrecompileErrors::Fatal {
267-
msg: format!("{}", error),
264+
let tag = vec[0];
265+
let tag = PrecompileTag::try_from(tag).map_err(|error| PrecompileErrors::Fatal {
266+
msg: format!("{error} when trying to convert tag={tag}"),
268267
})?;
269-
// We do not know how much gas was used.
270-
let gas_used = 0;
271-
let bytes = Bytes::copy_from_slice(&result);
272-
let result = PrecompileOutput { gas_used, bytes };
273-
Ok(result)
268+
match tag {
269+
PrecompileTag::TryCallApplication => {
270+
let target = u8_slice_to_application_id(&vec[1..33]);
271+
let argument = vec[33..].to_vec();
272+
let result = {
273+
let authenticated = true;
274+
let mut runtime = context
275+
.db
276+
.0
277+
.runtime
278+
.lock()
279+
.expect("The lock should be possible");
280+
runtime.try_call_application(authenticated, target, argument)
281+
}
282+
.map_err(|error| PrecompileErrors::Fatal {
283+
msg: format!("{}", error),
284+
})?;
285+
// We do not know how much gas was used.
286+
let gas_used = 0;
287+
let bytes = Bytes::copy_from_slice(&result);
288+
let result = PrecompileOutput { gas_used, bytes };
289+
Ok(result)
290+
}
291+
PrecompileTag::TryQueryApplication => Err(PrecompileErrors::Fatal {
292+
msg: "try_query_application is not available in the GeneralContractCall"
293+
.to_string(),
294+
}),
295+
}
274296
}
275297
}
276298

@@ -287,25 +309,36 @@ impl<Runtime: ServiceRuntime>
287309
context: &mut InnerEvmContext<WrapDatabaseRef<&mut DatabaseRuntime<Runtime>>>,
288310
) -> PrecompileResult {
289311
let vec = input.to_vec();
290-
let target = u8_slice_to_application_id(&vec[0..32]);
291-
let argument: Vec<u8> = vec[32..].to_vec();
292-
let result = {
293-
let mut runtime = context
294-
.db
295-
.0
296-
.runtime
297-
.lock()
298-
.expect("The lock should be possible");
299-
runtime.try_query_application(target, argument)
300-
}
301-
.map_err(|error| PrecompileErrors::Fatal {
302-
msg: format!("{}", error),
312+
let tag = vec[0];
313+
let tag = PrecompileTag::try_from(tag).map_err(|error| PrecompileErrors::Fatal {
314+
msg: format!("{error} when trying to convert tag={tag}"),
303315
})?;
304-
// We do not know how much gas was used.
305-
let gas_used = 0;
306-
let bytes = Bytes::copy_from_slice(&result);
307-
let result = PrecompileOutput { gas_used, bytes };
308-
Ok(result)
316+
match tag {
317+
PrecompileTag::TryCallApplication => Err(PrecompileErrors::Fatal {
318+
msg: "try_call_application is not available in the GeneralServiceCall".to_string(),
319+
}),
320+
PrecompileTag::TryQueryApplication => {
321+
let target = u8_slice_to_application_id(&vec[1..33]);
322+
let argument = vec[33..].to_vec();
323+
let result = {
324+
let mut runtime = context
325+
.db
326+
.0
327+
.runtime
328+
.lock()
329+
.expect("The lock should be possible");
330+
runtime.try_query_application(target, argument)
331+
}
332+
.map_err(|error| PrecompileErrors::Fatal {
333+
msg: format!("{}", error),
334+
})?;
335+
// We do not know how much gas was used.
336+
let gas_used = 0;
337+
let bytes = Bytes::copy_from_slice(&result);
338+
let result = PrecompileOutput { gas_used, bytes };
339+
Ok(result)
340+
}
341+
}
309342
}
310343
}
311344

linera-execution/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ pub use crate::{
7676
transaction_tracker::{TransactionOutcome, TransactionTracker},
7777
};
7878

79+
/// The `linera.sol` library code to be included in solidity smart
80+
/// contracts using Linera features.
81+
pub const LINERA_SOL: &str = include_str!("../solidity/linera.sol");
82+
7983
/// The maximum length of a stream name.
8084
const MAX_STREAM_NAME_LEN: usize = 64;
8185

linera-execution/src/test_utils/solidity.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use anyhow::Context;
1414
use serde_json::Value;
1515
use tempfile::{tempdir, TempDir};
1616

17+
use crate::LINERA_SOL;
18+
1719
fn write_compilation_json(path: &Path, file_name: &str) -> anyhow::Result<()> {
1820
let mut source = File::create(path).unwrap();
1921
writeln!(
@@ -78,6 +80,13 @@ fn get_bytecode_path(path: &Path, file_name: &str, contract_name: &str) -> anyho
7880
pub fn get_bytecode(source_code: &str, contract_name: &str) -> anyhow::Result<Vec<u8>> {
7981
let dir = tempdir().unwrap();
8082
let path = dir.path();
83+
if source_code.contains("linera.sol") {
84+
// The source code seems to import linera.sol, so let us write it in the code
85+
let file_name = "linera.sol";
86+
let test_code_path = path.join(file_name);
87+
let mut test_code_file = File::create(&test_code_path)?;
88+
writeln!(test_code_file, "{}", LINERA_SOL)?;
89+
}
8190
let file_name = "test_code.sol";
8291
let test_code_path = path.join(file_name);
8392
let mut test_code_file = File::create(&test_code_path)?;

linera-service/tests/fixtures/evm_call_wasm_example_counter.sol

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pragma solidity ^0.8.0;
1313
// solidity code. See,
1414
// TODO(#3761): Implement the Linera functionalities in a linera.sol library.
1515

16+
import "./linera.sol";
17+
1618
contract ExampleCallWasmCounter {
1719
bytes32 universal_address;
1820
constructor(bytes32 _universal_address) {
@@ -121,9 +123,7 @@ contract ExampleCallWasmCounter {
121123
address precompile = address(0x0b);
122124
CounterOperation memory input2 = CounterOperation({choice: 0, increment: input1});
123125
bytes memory input3 = bcs_serialize_CounterOperation(input2);
124-
bytes memory input4 = abi.encodePacked(universal_address, input3);
125-
(bool success, bytes memory return1) = precompile.call(input4);
126-
require(success);
126+
bytes memory return1 = Linera.try_call_application(universal_address, input3);
127127
uint64 return2 = bcs_deserialize_uint64(return1);
128128
return return2;
129129
}
@@ -132,9 +132,7 @@ contract ExampleCallWasmCounter {
132132
address precompile = address(0x0b);
133133
CounterRequest memory input2 = CounterRequest({choice:0, increment: 0});
134134
bytes memory input3 = serde_json_serialize_CounterRequest(input2);
135-
bytes memory input4 = abi.encodePacked(universal_address, input3);
136-
(bool success, bytes memory return1) = precompile.call(input4);
137-
require(success);
135+
bytes memory return1 = Linera.try_query_application(universal_address, input3);
138136
uint64 return2 = serde_json_deserialize_u64(return1);
139137
return return2;
140138
}

0 commit comments

Comments
 (0)