Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions packages/api-evm/source/actions/eth-estimate-gas.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import crypto from "node:crypto";

import { inject, injectable, tagged } from "@mainsail/container";
import { Contracts, Exceptions, Identifiers } from "@mainsail/contracts";
import { assert } from "@mainsail/utils";
Expand All @@ -23,7 +21,7 @@
@injectable()
export class EthEstimateGasAction implements Contracts.Api.RPC.Action {
@inject(Identifiers.Evm.Instance)
@tagged("instance", "validator")
@tagged("instance", "rpc")

Check warning on line 24 in packages/api-evm/source/actions/eth-estimate-gas.ts

View check run for this annotation

Codecov / codecov/patch

packages/api-evm/source/actions/eth-estimate-gas.ts#L24

Added line #L24 was not covered by tests
private readonly evm!: Contracts.Evm.Instance;

@inject(Identifiers.Cryptography.Configuration)
Expand Down Expand Up @@ -93,7 +91,6 @@
nonce: accountInfo.nonce,
specId: evmSpec,
to: data.to,
txHash: this.#generateTxHash(),
value: data.value ? BigInt(data.value) : BigInt(0),
};

Expand Down Expand Up @@ -165,21 +162,12 @@
return `0x${maxGasLimit.toString(16)}`;
}

async #execute(context: Contracts.Evm.TransactionContext): Promise<EstimateOutcome> {
await this.evm.prepareNextCommit({
commitKey: context.blockContext.commitKey,
});

async #execute(context: Contracts.Evm.TransactionSimulateContext): Promise<EstimateOutcome> {
try {
const { receipt } = await this.evm.process(context);
const { receipt } = await this.evm.simulate(context);

Check warning on line 167 in packages/api-evm/source/actions/eth-estimate-gas.ts

View check run for this annotation

Codecov / codecov/patch

packages/api-evm/source/actions/eth-estimate-gas.ts#L167

Added line #L167 was not covered by tests
return { receipt, success: receipt.status === 1 };
} catch (error) {
return { executionError: error.message, success: false };
}
}

#generateTxHash = () => {
const randomBytes = crypto.randomBytes(32);
return crypto.createHash("sha256").update(randomBytes).digest("hex");
};
}
18 changes: 17 additions & 1 deletion packages/contracts/source/contracts/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export interface GenesisInfo {

export interface ProcessResult {
readonly receipt: TransactionReceipt;
readonly mocked?: boolean;
}

export interface SimulateResult {
readonly receipt: TransactionReceipt;
}

export interface ViewResult {
Expand Down Expand Up @@ -117,6 +120,19 @@ export interface TransactionContext {
readonly specId: SpecId;
}

export interface TransactionSimulateContext {
readonly from: string;
/** Omit recipient when deploying a contract */
readonly to?: string;
readonly gasLimit: bigint;
readonly value: bigint;
readonly gasPrice: bigint;
readonly nonce: bigint;
readonly data: Buffer;
readonly blockContext: BlockContext;
readonly specId: SpecId;
}

export interface TransactionViewContext {
readonly from: string;
readonly to: string;
Expand Down
3 changes: 3 additions & 0 deletions packages/contracts/source/contracts/evm/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
PreverifyTransactionContext,
PreverifyTransactionResult,
ProcessResult,
SimulateResult,
TransactionContext,
TransactionSimulateContext,
TransactionViewContext,
UpdateRewardsAndVotesContext,
ViewResult,
Expand All @@ -25,6 +27,7 @@ export interface Instance extends CommitHandler {
prepareNextCommit(context: PrepareNextCommitContext): Promise<void>;
preverifyTransaction(txContext: PreverifyTransactionContext): Promise<PreverifyTransactionResult>;
process(txContext: TransactionContext): Promise<ProcessResult>;
simulate(txContext: TransactionSimulateContext): Promise<SimulateResult>;
view(viewContext: TransactionViewContext): Promise<ViewResult>;
initializeGenesis(commit: GenesisInfo): Promise<void>;
getAccountInfo(address: string, height?: bigint): Promise<AccountInfo>;
Expand Down
4 changes: 4 additions & 0 deletions packages/evm-service/source/instances/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
return this.#evm.process(txContext);
}

public async simulate(txContext: Contracts.Evm.TransactionSimulateContext): Promise<Contracts.Evm.SimulateResult> {
return this.#evm.simulate(txContext);
}

Check warning on line 91 in packages/evm-service/source/instances/evm.ts

View check run for this annotation

Codecov / codecov/patch

packages/evm-service/source/instances/evm.ts#L90-L91

Added lines #L90 - L91 were not covered by tests

public async initializeGenesis(info: Contracts.Evm.GenesisInfo): Promise<void> {
return this.#evm.initializeGenesis({
account: info.account,
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ members = ["core", "bindings"]

[workspace.package]
version = "0.1.0"
rust-version = "1.85"
rust-version = "1.88"
edition = "2024"
license = "GPL-3.0-only"
authors = [""]
Expand Down
74 changes: 74 additions & 0 deletions packages/evm/bindings/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@
pub spec_id: JsString,
}

#[napi(object)]

Check warning on line 37 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L37

Added line #L37 was not covered by tests
pub struct JsTransactionSimulateContext {
pub from: JsString,
/// Omit recipient when deploying a contract
pub to: Option<JsString>,
pub gas_limit: JsBigInt,
pub gas_price: JsBigInt,
pub value: JsBigInt,
pub nonce: JsBigInt,
pub data: JsBuffer,
pub block_context: JsBlockContext,
pub spec_id: JsString,
}

#[napi(object)]
pub struct JsPreverifyTransactionContext {
pub from: JsString,
Expand Down Expand Up @@ -163,6 +177,19 @@
pub gas_limit: Option<u64>,
}

#[derive(Debug)]
pub struct TxSimulateContext {
pub from: Address,
pub to: Option<Address>,
pub gas_limit: u64,
pub gas_price: u128,
pub value: U256,
pub nonce: u64,
pub data: Bytes,
pub block_context: BlockContext,
pub spec_id: SpecId,
}

#[derive(Debug)]
pub struct BlockContext {
pub commit_key: CommitKey,
Expand Down Expand Up @@ -217,6 +244,7 @@
pub tx_hash: Option<B256>,
pub block_context: Option<BlockContext>,
pub spec_id: SpecId,
pub stateful: bool,
}

impl From<TxViewContext> for ExecutionContext {
Expand All @@ -232,6 +260,7 @@
tx_hash: None,
block_context: None,
spec_id: value.spec_id,
stateful: false,

Check warning on line 263 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L263

Added line #L263 was not covered by tests
}
}
}
Expand All @@ -249,6 +278,25 @@
tx_hash: Some(value.tx_hash),
block_context: Some(value.block_context),
spec_id: value.spec_id,
stateful: true,
}
}

Check warning on line 283 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L281-L283

Added lines #L281 - L283 were not covered by tests
}

impl From<TxSimulateContext> for ExecutionContext {
fn from(value: TxSimulateContext) -> Self {
Self {
from: value.from,
to: value.to,
gas_limit: Some(value.gas_limit),
gas_price: value.gas_price,
value: value.value,
nonce: Some(value.nonce),
data: value.data,
tx_hash: None,
block_context: Some(value.block_context),
spec_id: value.spec_id,
stateful: false,

Check warning on line 299 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L287-L299

Added lines #L287 - L299 were not covered by tests
}
}
}
Expand Down Expand Up @@ -367,6 +415,32 @@
}
}

impl TryFrom<JsTransactionSimulateContext> for TxSimulateContext {
type Error = anyhow::Error;

fn try_from(mut value: JsTransactionSimulateContext) -> std::result::Result<Self, Self::Error> {
let buf = value.data.into_value()?;

Check warning on line 422 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L421-L422

Added lines #L421 - L422 were not covered by tests

let to = if let Some(to) = value.to {
Some(utils::create_address_from_js_string(to)?)

Check warning on line 425 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L424-L425

Added lines #L424 - L425 were not covered by tests
} else {
None

Check warning on line 427 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L427

Added line #L427 was not covered by tests
};

Ok(TxSimulateContext {
to,
gas_limit: value.gas_limit.try_into()?,
gas_price: value.gas_price.get_u128()?.1,
from: utils::create_address_from_js_string(value.from)?,
value: utils::convert_bigint_to_u256(value.value)?,
nonce: value.nonce.get_u64()?.0,
data: Bytes::from(buf.as_ref().to_owned()),
block_context: value.block_context.try_into()?,
spec_id: parse_spec_id(value.spec_id)?,

Check warning on line 439 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L431-L439

Added lines #L431 - L439 were not covered by tests
})
}

Check warning on line 441 in packages/evm/bindings/src/ctx.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/ctx.rs#L441

Added line #L441 was not covered by tests
}

impl TryFrom<JsPreverifyTransactionContext> for PreverifyTxContext {
type Error = anyhow::Error;

Expand Down
95 changes: 70 additions & 25 deletions packages/evm/bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
BlockContext, CalculateRoundValidatorsContext, EvmOptions, ExecutionContext, GenesisContext,
JsCalculateRoundValidatorsContext, JsCommitData, JsCommitKey, JsEvmOptions, JsGenesisContext,
JsPrepareNextCommitContext, JsPreverifyTransactionContext, JsTransactionContext,
JsTransactionViewContext, JsUpdateRewardsAndVotesContext, PrepareNextCommitContext,
PreverifyTxContext, TxContext, TxViewContext, UpdateRewardsAndVotesContext,
JsTransactionSimulateContext, JsTransactionViewContext, JsUpdateRewardsAndVotesContext,
PrepareNextCommitContext, PreverifyTxContext, TxContext, TxSimulateContext, TxViewContext,
UpdateRewardsAndVotesContext,
};
use logger::JsLogger;
use mainsail_evm_core::{
Expand Down Expand Up @@ -254,6 +255,7 @@
gas_price: 0,
spec_id: ctx.spec_id,
tx_hash: None,
stateful: true,

Check warning on line 258 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L258

Added line #L258 was not covered by tests
}) {
Ok(receipt) => {
self.logger.log(
Expand Down Expand Up @@ -339,6 +341,7 @@
gas_price: 0,
spec_id: ctx.spec_id,
tx_hash: None,
stateful: true,

Check warning on line 344 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L344

Added line #L344 was not covered by tests
}) {
Ok(receipt) => {
self.logger.log(
Expand Down Expand Up @@ -662,6 +665,13 @@
}
}

pub fn simulate(
&mut self,
ctx: TxSimulateContext,
) -> std::result::Result<TxReceipt, EVMError<String>> {
self.execute(ctx.into())
}

Check warning on line 673 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L668-L673

Added lines #L668 - L673 were not covered by tests

pub fn process(
&mut self,
tx_ctx: TxContext,
Expand Down Expand Up @@ -721,27 +731,7 @@
}
}

let result = self.transact_evm(tx_ctx.into());

match result {
Ok(result) => {
let receipt = map_execution_result(result);
Ok(receipt)
}
Err(err) => {
match err {
EVMError::Transaction(err) => {
return Err(EVMError::Transaction(err));
}
// EVMError::Header(_) => todo!(),
// EVMError::Database(_) => todo!(),
// EVMError::Custom(_) => todo!(),
_ => {
unimplemented!("fatal evm err {:?}", err);
}
}
}
}
self.execute(tx_ctx.into())

Check warning on line 734 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L734

Added line #L734 was not covered by tests
}

pub fn commit(
Expand Down Expand Up @@ -947,13 +937,40 @@
Ok(())
}

fn execute(
&mut self,
ctx: ExecutionContext,
) -> std::result::Result<TxReceipt, EVMError<String>> {
match self.transact_evm(ctx.into()) {
Ok(result) => {
let receipt = map_execution_result(result);
Ok(receipt)

Check warning on line 947 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L940-L947

Added lines #L940 - L947 were not covered by tests
}
Err(err) => {
match err {
EVMError::Transaction(err) => {
return Err(EVMError::Transaction(err));

Check warning on line 952 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L949-L952

Added lines #L949 - L952 were not covered by tests
}
// EVMError::Header(_) => todo!(),
// EVMError::Database(_) => todo!(),
// EVMError::Custom(_) => todo!(),
_ => {
panic!("fatal evm err {:?}", err);

Check warning on line 958 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L958

Added line #L958 was not covered by tests
}
}
}
}
}

Check warning on line 963 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L963

Added line #L963 was not covered by tests

fn transact_evm(
&mut self,
ctx: ExecutionContext,
) -> std::result::Result<ExecutionResult, EVMError<mainsail_evm_core::db::Error>> {
let mut state_builder = State::builder().with_bundle_update();

if let Some(commit_key) = ctx.block_context.as_ref().map(|b| &b.commit_key) {
if let Some(commit_key) = ctx.block_context.as_ref().map(|b| &b.commit_key)
&& ctx.stateful

Check warning on line 972 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L971-L972

Added lines #L971 - L972 were not covered by tests
{
if let Some(pending_commit) = self.pending_commits.get_mut(commit_key) {
state_builder =
state_builder.with_cached_prestate(std::mem::take(&mut pending_commit.cache));
Expand Down Expand Up @@ -1004,7 +1021,9 @@
let ResultAndState { state, result } = result;

// Update state if transaction is part of a commit
if let Some(commit_key) = ctx.block_context.as_ref().map(|b| &b.commit_key) {
if let Some(commit_key) = ctx.block_context.as_ref().map(|b| &b.commit_key)
&& ctx.stateful

Check warning on line 1025 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L1024-L1025

Added lines #L1024 - L1025 were not covered by tests
{
let state_db = evm.db_mut();
state_db.commit(state);

Expand Down Expand Up @@ -1140,6 +1159,19 @@
)
}

#[napi(ts_return_type = "Promise<JsSimulateResult>")]
pub fn simulate(
&mut self,
node_env: Env,
tx_ctx: JsTransactionSimulateContext,
) -> Result<JsObject> {
let tx_ctx = TxSimulateContext::try_from(tx_ctx)?;
node_env.execute_tokio_future(
Self::simulate_async(self.evm.clone(), tx_ctx),
|&mut node_env, result| Ok(result::JsSimulateResult::new(&node_env, result)?),

Check warning on line 1171 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L1163-L1171

Added lines #L1163 - L1171 were not covered by tests
)
}

Check warning on line 1173 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L1173

Added line #L1173 was not covered by tests

#[napi(ts_return_type = "Promise<void>")]
pub fn initialize_genesis(
&mut self,
Expand Down Expand Up @@ -1601,6 +1633,19 @@
}
}

async fn simulate_async(
evm: Arc<tokio::sync::Mutex<EvmInner>>,
tx_ctx: TxSimulateContext,
) -> Result<TxReceipt> {
let mut lock = evm.lock().await;
let result = lock.simulate(tx_ctx);

Check warning on line 1641 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L1636-L1641

Added lines #L1636 - L1641 were not covered by tests

match result {
Ok(result) => Result::Ok(result),
Err(err) => Result::Err(serde::de::Error::custom(err)),

Check warning on line 1645 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L1643-L1645

Added lines #L1643 - L1645 were not covered by tests
}
}

Check warning on line 1647 in packages/evm/bindings/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

packages/evm/bindings/src/lib.rs#L1647

Added line #L1647 was not covered by tests

async fn get_account_info_async(
evm: Arc<tokio::sync::Mutex<EvmInner>>,
address: Address,
Expand Down
Loading
Loading