Skip to content

Commit

Permalink
configure evm block
Browse files Browse the repository at this point in the history
  • Loading branch information
oXtxNt9U committed Mar 4, 2024
1 parent bffb90d commit 66bd718
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 28 deletions.
9 changes: 9 additions & 0 deletions packages/contracts/source/contracts/evm/evm.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
export interface Instance {
configureBlockEnvironment(environment: BlockEnvironment): Promise<void>;
transact(txContext: TransactionContext): Promise<TransactionResult>;
view(txContext: TransactionContext): Promise<TransactionResult>;
}

export interface BlockEnvironment {
number: bigint;
timestamp: bigint;
gasLimit: bigint;
basefee: bigint;
difficulty: bigint;
}

export interface TransactionContext {
caller: string;
/** Omit recipient when deploying a contract */
Expand Down
33 changes: 33 additions & 0 deletions packages/evm/bindings/src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use napi::JsBigInt;
use napi_derive::napi;
use revm::primitives::{Address, BlobExcessGasAndPrice, BlockEnv, B256};

use crate::utils;

#[napi(object)]
pub struct JsBlockEnv {
pub number: JsBigInt,
pub timestamp: JsBigInt,
pub gas_limit: JsBigInt,
pub basefee: JsBigInt,
pub difficulty: JsBigInt,
}

impl TryInto<BlockEnv> for JsBlockEnv {
type Error = anyhow::Error;

fn try_into(mut self) -> std::result::Result<BlockEnv, Self::Error> {
Ok(BlockEnv {
number: utils::convert_bigint_to_u256(&mut self.number)?,
timestamp: utils::convert_bigint_to_u256(&mut self.timestamp)?,
gas_limit: utils::convert_bigint_to_u256(&mut self.gas_limit)?,
basefee: utils::convert_bigint_to_u256(&mut self.basefee)?,
difficulty: utils::convert_bigint_to_u256(&mut self.difficulty)?,

// TODO: double check
coinbase: Address::ZERO,
blob_excess_gas_and_price: Some(BlobExcessGasAndPrice::new(0)),
prevrandao: Some(B256::ZERO),
})
}
}
67 changes: 45 additions & 22 deletions packages/evm/bindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::{str::FromStr, sync::Arc};

use block::JsBlockEnv;
use ctx::{JsTransactionContext, TxContext};
use mainsail_evm_core::EvmInstance;
use napi::{bindgen_prelude::*, JsBigInt, JsBuffer, JsObject, JsString};
use napi::{bindgen_prelude::*, JsBigInt, JsObject, JsString};
use napi_derive::napi;
use result::{JsTransactionResult, TxResult};
use revm::primitives::{AccountInfo, Address, Bytes, ExecutionResult, Log, U256};
use result::TxResult;
use revm::primitives::{AccountInfo, Address, BlockEnv, ExecutionResult, U256};

mod block;
mod ctx;
mod result;
mod utils;
Expand Down Expand Up @@ -96,6 +98,19 @@ impl EvmInner {
self.transact_evm(tx_ctx, false)
}

pub fn configure_block_env(&mut self, block_env: revm::primitives::BlockEnv) {
let mut evm = self.evm_instance.take().expect("ok");

evm = evm
.modify()
.modify_block_env(|env: &mut revm::primitives::BlockEnv| {
*env = block_env;
})
.build();

self.evm_instance.replace(evm);
}

fn transact_evm(&mut self, tx_ctx: TxContext, commit: bool) -> TxResult {
let mut evm = self.evm_instance.take().expect("ok");

Expand Down Expand Up @@ -169,7 +184,10 @@ impl EvmInner {
output: None,
},
},
Err(_err) => todo!(), // TODO: should never happen?
Err(_err) => {
println!("transact_evm err: {:?}", _err);
todo!() // TODO: should never happen?
}
}
}
}
Expand Down Expand Up @@ -215,6 +233,19 @@ impl JsEvmWrapper {
)
}

#[napi(ts_return_type = "Promise<void>")]
pub fn configure_block_env(
&mut self,
node_env: Env,
block_env: JsBlockEnv,
) -> Result<JsObject> {
let block_env = TryInto::try_into(block_env)?;
node_env.execute_tokio_future(
Self::configure_block_env_async(self.evm.clone(), block_env),
|&mut _, _| Ok(()),
)
}

#[napi(ts_return_type = "Promise<JsAccountInfo>")]
pub fn get_account_info(&mut self, node_env: Env, address: String) -> Result<JsObject> {
let address = Address::from_str(&address).unwrap();
Expand Down Expand Up @@ -256,6 +287,15 @@ impl JsEvmWrapper {
Ok(result)
}

async fn configure_block_env_async(
evm: Arc<tokio::sync::Mutex<EvmInner>>,
block_env: BlockEnv,
) -> Result<()> {
let mut lock = evm.lock().await;
let result = lock.configure_block_env(block_env);
Ok(result)
}

async fn get_account_info_async(
evm: Arc<tokio::sync::Mutex<EvmInner>>,
address: Address,
Expand Down Expand Up @@ -291,7 +331,7 @@ impl JsEvmWrapper {
address: node_env
.create_string_from_std(address.to_string())
.unwrap(),
balance: convert_u256_to_bigint(node_env, account_info.balance),
balance: utils::convert_u256_to_bigint(node_env, account_info.balance),
nonce: node_env.create_bigint_from_u64(account_info.nonce).unwrap(),
},
None => JsAccountInfo {
Expand All @@ -304,20 +344,3 @@ impl JsEvmWrapper {
}
}
}

fn convert_u256_to_bigint(node_env: Env, value: U256) -> JsBigInt {
let slice = value.as_le_slice();

const WORD_SIZE: usize = 8;
assert!(slice.len() % WORD_SIZE == 0);

// https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words
let mut words: Vec<u64> = Vec::with_capacity(slice.len() / WORD_SIZE);
for chunk in slice.chunks_exact(WORD_SIZE) {
let mut bytes = [0; 8];
bytes.copy_from_slice(chunk);
words.push(u64::from_le_bytes(bytes));
}

node_env.create_bigint_from_words(false, words).unwrap()
}
38 changes: 36 additions & 2 deletions packages/evm/bindings/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
use std::str::FromStr;

use anyhow;
use napi::JsString;
use revm::primitives::Address;
use napi::{Env, JsBigInt, JsString};
use revm::primitives::{Address, U256};

pub(crate) fn create_address_from_js_string(js_str: JsString) -> anyhow::Result<Address> {
let js_str = js_str.into_utf8()?;
let slice = js_str.as_str()?;
Ok(Address::from_str(slice)?)
}

pub(crate) fn convert_u256_to_bigint(node_env: Env, value: U256) -> JsBigInt {
let slice = value.as_le_slice();

const WORD_SIZE: usize = 8;
assert!(slice.len() % WORD_SIZE == 0);

// https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words
let mut words: Vec<u64> = Vec::with_capacity(slice.len() / WORD_SIZE);
for chunk in slice.chunks_exact(WORD_SIZE) {
let mut bytes = [0; 8];
bytes.copy_from_slice(chunk);
words.push(u64::from_le_bytes(bytes));
}

node_env.create_bigint_from_words(false, words).unwrap()
}

pub(crate) fn convert_bigint_to_u256(bigint: &mut JsBigInt) -> anyhow::Result<U256> {
const WORD_SIZE: usize = 8;

let words = bigint.get_words()?;

let mut bytes: Vec<u8> = Vec::with_capacity(bigint.word_count * WORD_SIZE);

for word in words.1 {
let word_bytes = word.to_le_bytes();
bytes.extend_from_slice(&word_bytes);
}

assert!(bytes.len() % WORD_SIZE == 0);

Ok(U256::from_le_slice(&bytes[..]))
}
13 changes: 9 additions & 4 deletions packages/evm/source/instance.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { injectable } from "@mainsail/container";
import { Contracts } from "@mainsail/contracts";

import { Evm, JsTransactionContext, JsTransactionResult } from "./generated/bindings";
import { Evm } from "./generated/bindings";

@injectable()
export class Instance {
export class Instance implements Contracts.Evm.Instance {
private readonly evm: Evm = new Evm();

public async transact(txContext: JsTransactionContext): Promise<JsTransactionResult> {
public async configureBlockEnvironment(environment: Contracts.Evm.BlockEnvironment): Promise<void> {
return this.evm.configureBlockEnv(environment);
}

public async transact(txContext: Contracts.Evm.TransactionContext): Promise<Contracts.Evm.TransactionResult> {
return this.evm.transact(txContext);
}

public async view(txContext: JsTransactionContext): Promise<JsTransactionResult> {
public async view(txContext: Contracts.Evm.TransactionContext): Promise<Contracts.Evm.TransactionResult> {
return this.evm.view(txContext);
}
}
22 changes: 22 additions & 0 deletions packages/processor/source/block-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export class BlockProcessor implements Contracts.Processor.BlockProcessor {
@inject(Identifiers.Database.Service)
private readonly databaseService!: Contracts.Database.DatabaseService;

@inject(Identifiers.Evm.Instance)
private readonly evm!: Contracts.Evm.Instance;

@inject(Identifiers.TransactionPool.Service)
private readonly transactionPool!: Contracts.TransactionPool.Service;

Expand Down Expand Up @@ -55,6 +58,8 @@ export class BlockProcessor implements Contracts.Processor.BlockProcessor {
try {
await this.verifier.verify(unit);

await this.#applyBlockToEvm(unit);

for (const transaction of unit.getBlock().transactions) {
await this.transactionProcessor.process(unit.store.walletRepository, transaction);
}
Expand Down Expand Up @@ -160,4 +165,21 @@ export class BlockProcessor implements Contracts.Processor.BlockProcessor {
await validatorMutator.apply(walletRepository, forgerWallet, block.data);
}
}

async #applyBlockToEvm(unit: Contracts.Processor.ProcessableUnit) {
const block = unit.getBlock();

return this.evm.configureBlockEnvironment({
// TODO: milestone
basefee: 0n,
difficulty: 0n,

// u256::max (only for testing)
gasLimit:
115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_913_129_639_935n,

number: BigInt(block.header.height),
timestamp: BigInt(Math.floor(block.header.timestamp / 1000)),
});
}
}

0 comments on commit 66bd718

Please sign in to comment.