Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Evm a part of the supported virtual machines. #3386

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

MathieuDutSik
Copy link
Contributor

@MathieuDutSik MathieuDutSik commented Feb 21, 2025

Motivation

We extend here the support for EVM. This requires no longer making the virtual machine
a feature of the storage but instead a part of the BytecodeId.

Proposal

The following changes have been done:

  • A type VmRuntime has been introduced that covers both Wasm (Native linera) and Evm.
  • The VmRuntime is added to the BytecodeId. This is a reasonable way to make the VM a part of the type description.
  • We cannot put the VmRuntime in the UserApplicationDescription since this forces the create_application to have a VmRuntime type. While innocent, this changes is not adequate since the create_application is used in smart contract code and we want to avoid this part to seep into the smart contract code.
  • The VmRuntime, does not depend on any feature. This is because they have to pass the wit barrier and the wit-types cannot depend on features.
  • The VmRuntime has been added to the linera-base. This is unavoidable because BytecodeId depends on it.
  • The load_contract/load_service is simplified into one single code that panics when relevant features have not been installed.
  • The --vm-runtime is added to the function calls like publish_application. When missing it defaults to VmRuntime::Wasm.
  • The default_with_stabilizer is eliminated since it is not used.
  • A typo in contract_runtime_apis.rs for ApplicationDescription is corrected.

Test Plan

The CI. No test has been added to test the functionality.

Release Plan

Normal release plan.

Links

None.

@MathieuDutSik MathieuDutSik marked this pull request as ready for review February 22, 2025 20:41
@@ -47,9 +48,14 @@ pub fn create_dummy_user_application_description(
compressed_bytes: b"service".to_vec(),
});

let vm_runtime = VmRuntime::default();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just inline the value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, done.

@@ -350,6 +351,8 @@ pub struct BytecodeId<Abi = (), Parameters = (), InstantiationArgument = ()> {
pub contract_blob_hash: CryptoHash,
/// The hash of the blob containing the service bytecode.
pub service_blob_hash: CryptoHash,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@afck @jvff Should we make the service blob optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, if so it would have to be in a separate PR I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that makes sense. Or even something like:

pub enum BytecodeId {
    Wasm {
        contract_blob_hash: CryptoHash,
        service_blob_hash: CryptoHash,
    },
    Evm {
        bytecode_hash: CryptoHash,
    },
}

Or even just have one blob per bytecode, which for Wasm could either be:

  • a tar archive with the contract and the service together,
  • a blob index listing which blobs make up the contract and which ones make the service

The latter would allow for larger applications, and could store the VM runtime inside the blob itself 🤔

We can discuss to decide on an alternative and implement it in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach ^^, even if this may create bit more boilerplate. @afck What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the tar idea; except I wonder whether it creates a lot of overhead if we have to always load both bytecode modules even if we use only one.

Comment on lines 41 to +44
record bytecode-id {
contract-blob-hash: crypto-hash,
service-blob-hash: crypto-hash,
vm-runtime: vm-runtime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@afck @jvff I'm worried that we expose too much detail about BytecodeID (and generally applications) to the SDK. Then, we will have to maintain them for ever.

Perhaps, this BytecodeID could be a hexadecimal string. Alternatively, we could also remove create_application for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could change. But in another PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's not ideal and will make adding new runtimes harder because it will always require changing the WIT interface.

I'm starting to lean more towards having a single bytecode blob with metadata including the VM runtime and pointing to the actual blobs to form the bytecode.

But yeah, it doesn't have to be done in this PR.

@@ -773,6 +774,10 @@ pub enum ClientCommand {
/// Path to the Wasm file for the application "service" bytecode.
service: PathBuf,

/// The virtual machine runtime to use.
#[arg(long)]
vm_runtime: Option<VmRuntime>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We could also handle the default here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, thank you.

@@ -1344,19 +1344,6 @@ pub trait WithWasmDefault {
}

impl WasmRuntime {
#[cfg(with_wasm_runtime)]
pub fn default_with_sanitizer() -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Is this unused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@ma2bd ma2bd requested review from afck and jvff February 23, 2025 17:52
@ma2bd
Copy link
Contributor

ma2bd commented Feb 23, 2025

@MathieuDutSik LGTM. I believe you need to update the summary. Letting someone else accept for good

@@ -350,6 +351,8 @@ pub struct BytecodeId<Abi = (), Parameters = (), InstantiationArgument = ()> {
pub contract_blob_hash: CryptoHash,
/// The hash of the blob containing the service bytecode.
pub service_blob_hash: CryptoHash,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that makes sense. Or even something like:

pub enum BytecodeId {
    Wasm {
        contract_blob_hash: CryptoHash,
        service_blob_hash: CryptoHash,
    },
    Evm {
        bytecode_hash: CryptoHash,
    },
}

Or even just have one blob per bytecode, which for Wasm could either be:

  • a tar archive with the contract and the service together,
  • a blob index listing which blobs make up the contract and which ones make the service

The latter would allow for larger applications, and could store the VM runtime inside the blob itself 🤔

We can discuss to decide on an alternative and implement it in a separate PR.

@@ -674,10 +690,15 @@ impl<'de, Abi, Parameters, InstantiationArgument> Deserialize<'de>

impl BytecodeId {
/// Creates a bytecode ID from contract/service hashes.
pub fn new(contract_blob_hash: CryptoHash, service_blob_hash: CryptoHash) -> Self {
pub fn new(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Also need to update the documentation above, maybe with:

Creates a bytecode ID from contract/service hashes and the VM runtime to use.

fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"wasm" => Ok(VmRuntime::Wasm),
"revm" => Ok(VmRuntime::Evm),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think this should be "evm", because if I understood correctly revm is an implementation of the Evm (like Wasmer is an implementation of Wasm) 🤔

Suggested change
"revm" => Ok(VmRuntime::Evm),
"evm" => Ok(VmRuntime::Evm),

Comment on lines 41 to +44
record bytecode-id {
contract-blob-hash: crypto-hash,
service-blob-hash: crypto-hash,
vm-runtime: vm-runtime,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's not ideal and will make adding new runtimes harder because it will always require changing the WIT interface.

I'm starting to lean more towards having a single bytecode blob with metadata including the VM runtime and pointing to the actual blobs to form the bytecode.

But yeah, it doesn't have to be done in this PR.

} else {
panic!(
"An Evm runtime is required to load user applications. \
Please enable the `revm` feature flags \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: typo

Suggested change
Please enable the `revm` feature flags \
Please enable the `revm` feature flag \

Comment on lines +371 to +374
let evm_runtime = EvmRuntime::Revm;
Ok(EvmServiceModule::new(_service_bytecode, evm_runtime)
.await?
.into())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what it means to have an Evm service. We could either return an error saying Evm services are not supported, or figure out a way to query the Evm state, either exposing some read-only entrypoints or a single generic service to inspect the state.

} else {
panic!(
"An Evm runtime is required to load user applications. \
Please enable the `revm` feature flags \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: typo

Suggested change
Please enable the `revm` feature flags \
Please enable the `revm` feature flag \

@@ -278,41 +280,57 @@ pub trait Storage: Sized {
let compressed_contract_bytecode = CompressedBytecode {
compressed_bytes: contract_blob.into_bytes().to_vec(),
};
let contract_bytecode =
let _contract_bytecode =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add an attribute instead of the underscore. The pattern is to add the underscore to unused bindings, but in this case it might be used.

Suggested change
let _contract_bytecode =
#[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
let contract_bytecode =

Please enable the `wasmer` or the `wasmtime` feature flags \
when compiling `linera-storage`."
);
let _service_bytecode =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

Suggested change
let _service_bytecode =
#[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
let service_bytecode =

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants