Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.github
.claude
target
docs/node_modules
docs/.next
docs/dist
data
.data
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ just node-run

Default RPC endpoint: `http://localhost:8545`.

### Run `evd` (and optionally `ev-node`) with Docker Compose

Run `evd` only:

```bash
docker compose up --build evd
```

Run `evd` + `ev-node`:

```bash
docker compose -f docker-compose.yml -f docker-compose.ev-node.yml up --build
```

Notes:
- `evd` JSON-RPC is exposed on `http://localhost:8545`
- `evd` gRPC is exposed on `localhost:50051`
- `ev-node` gets `EVD_GRPC_ENDPOINT=evd:50051` in the compose network
- if your `ev-node` image needs explicit startup flags, override `command` for the `ev-node` service with an extra compose override file

## Documentation

Read the docs for implementation details instead of this README.
Expand Down
3 changes: 0 additions & 3 deletions bin/evd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ evolve_evnode = { workspace = true, features = ["testapp"] }
evolve_testapp.workspace = true
evolve_token.workspace = true
evolve_scheduler.workspace = true
evolve_fungible_asset.workspace = true
evolve_testing.workspace = true
evolve_tx_eth.workspace = true
evolve_chain_index.workspace = true
Expand All @@ -42,8 +41,6 @@ tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
borsh.workspace = true
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[lints]
workspace = true
130 changes: 34 additions & 96 deletions bin/evd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@
//! }
//! ```

mod genesis_config;

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Duration;
Expand All @@ -76,8 +74,7 @@ use evolve_chain_index::{
build_index_data, BlockMetadata, ChainIndex, ChainStateProvider, ChainStateProviderConfig,
PersistentChainIndex,
};
use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX;
use evolve_core::{AccountId, Environment, Message, ReadonlyKV, SdkResult};
use evolve_core::{AccountId, ReadonlyKV};
use evolve_eth_jsonrpc::{start_server_with_subscriptions, RpcServerConfig, SubscriptionManager};
use evolve_evnode::{EvnodeServer, EvnodeServerConfig, ExecutorServiceConfig, OnBlockExecuted};
use evolve_mempool::{new_shared_mempool, Mempool, SharedMempool};
Expand All @@ -92,6 +89,7 @@ use evolve_server::{
};
use evolve_stf_traits::{AccountsCodeStorage, StateChange};
use evolve_storage::{Operation, QmdbStorage, Storage, StorageConfig};
use evolve_testapp::genesis_config::{EvdGenesisConfig, EvdGenesisResult};
use evolve_testapp::{
build_mempool_stf, default_gas_config, do_genesis_inner, install_account_codes,
PLACEHOLDER_ACCOUNT,
Expand All @@ -101,8 +99,6 @@ use evolve_token::account::TokenRef;
use evolve_tx_eth::address_to_account_id;
use evolve_tx_eth::TxContext;

use genesis_config::{EvdGenesisConfig, EvdGenesisResult};

#[derive(Parser)]
#[command(name = "evd")]
#[command(about = "Evolve node daemon with gRPC execution layer")]
Expand Down Expand Up @@ -201,7 +197,7 @@ fn run_node(config: NodeConfig, genesis_config: Option<EvdGenesisConfig>) {
}
None => {
tracing::info!("No existing state found, running genesis...");
let output = run_genesis(&storage, &codes, genesis_config.as_ref()).await;
let output = run_genesis(&storage, &codes, genesis_config.as_ref());
commit_genesis(&storage, output.changes, &output.genesis_result)
.await
.expect("genesis commit failed");
Expand Down Expand Up @@ -439,7 +435,7 @@ fn init_genesis(data_dir: &str, genesis_config: Option<EvdGenesisConfig>) {
}

let codes = build_codes();
let output = run_genesis(&storage, &codes, genesis_config.as_ref()).await;
let output = run_genesis(&storage, &codes, genesis_config.as_ref());

commit_genesis(&storage, output.changes, &output.genesis_result)
.await
Expand All @@ -457,48 +453,14 @@ fn build_codes() -> AccountStorageMock {
codes
}

/// Pre-register an EOA account in storage so genesis can reference it.
fn build_eoa_registration(account_id: AccountId, eth_address: [u8; 20]) -> Vec<Operation> {
let mut ops = Vec::with_capacity(3);

// 1. Register account code identifier
let mut key = vec![ACCOUNT_IDENTIFIER_PREFIX];
key.extend_from_slice(&account_id.as_bytes());
let value = Message::new(&"EthEoaAccount".to_string())
.unwrap()
.into_bytes()
.unwrap();
ops.push(Operation::Set { key, value });

// 2. Set nonce = 0 (Item prefix 0)
let mut nonce_key = account_id.as_bytes().to_vec();
nonce_key.push(0u8);
let nonce_value = Message::new(&0u64).unwrap().into_bytes().unwrap();
ops.push(Operation::Set {
key: nonce_key,
value: nonce_value,
});

// 3. Set eth_address (Item prefix 1)
let mut addr_key = account_id.as_bytes().to_vec();
addr_key.push(1u8);
let addr_value = Message::new(&eth_address).unwrap().into_bytes().unwrap();
ops.push(Operation::Set {
key: addr_key,
value: addr_value,
});

ops
}

/// Run genesis using the default testapp genesis or a custom genesis config.
async fn run_genesis<S: ReadonlyKV + Storage>(
fn run_genesis<S: ReadonlyKV + Storage>(
storage: &S,
codes: &AccountStorageMock,
genesis_config: Option<&EvdGenesisConfig>,
) -> GenesisOutput<EvdGenesisResult> {
match genesis_config {
Some(config) => run_custom_genesis(storage, codes, config).await,
Some(config) => run_custom_genesis(storage, codes, config),
None => run_default_genesis(storage, codes),
}
}
Expand Down Expand Up @@ -534,56 +496,29 @@ fn run_default_genesis<S: ReadonlyKV + Storage>(
}

/// Custom genesis with ETH EOA accounts from a genesis JSON file.
async fn run_custom_genesis<S: ReadonlyKV + Storage>(
///
/// Registers funded EOA accounts via `EthEoaAccountRef::initialize` inside
/// `system_exec`, then initializes the token with their balances.
fn run_custom_genesis<S: ReadonlyKV + Storage>(
storage: &S,
codes: &AccountStorageMock,
genesis_config: &EvdGenesisConfig,
) -> GenesisOutput<EvdGenesisResult> {
use evolve_core::BlockContext;
use evolve_testapp::eth_eoa::eth_eoa_account::EthEoaAccountRef;

// Parse accounts that have a non-zero balance (need pre-registration for genesis funding).
// Other accounts are auto-registered by the STF on their first transaction.
let funded_accounts: Vec<(Address, u128)> = genesis_config
let funded_accounts: Vec<([u8; 20], u128)> = genesis_config
.accounts
.iter()
.filter(|acc| acc.balance > 0)
.map(|acc| {
let addr = acc
.parse_address()
.expect("invalid address in genesis config");
(addr, acc.balance)
(addr.into_array(), acc.balance)
})
.collect();

// Pre-register only funded EOA accounts in storage
let mut pre_ops = Vec::new();
for (addr, _) in &funded_accounts {
let id = address_to_account_id(*addr);
let addr_bytes: [u8; 20] = addr.into_array();
pre_ops.extend(build_eoa_registration(id, addr_bytes));
}

storage
.batch(pre_ops)
.await
.expect("pre-register EOAs failed");
storage.commit().await.expect("pre-register commit failed");

tracing::info!(
"Pre-registered {} funded EOA accounts:",
funded_accounts.len()
);
for (i, (addr, balance)) in funded_accounts.iter().enumerate() {
let id = address_to_account_id(*addr);
tracing::info!(" #{:02}: {:?} (0x{:x}) balance={}", i, id, addr, balance);
}

// Build balances list for genesis token initialization
let balances: Vec<(AccountId, u128)> = funded_accounts
.iter()
.map(|(addr, balance)| (address_to_account_id(*addr), *balance))
.collect();

let minter = AccountId::new(genesis_config.minter_id);
let metadata = genesis_config.token.to_metadata();

Expand All @@ -593,7 +528,27 @@ async fn run_custom_genesis<S: ReadonlyKV + Storage>(

let (genesis_result, state) = stf
.system_exec(storage, codes, genesis_block, |env| {
do_custom_genesis(metadata.clone(), balances.clone(), minter, env)
for (eth_addr, _) in &funded_accounts {
EthEoaAccountRef::initialize(*eth_addr, env)?;
}

let balances: Vec<(AccountId, u128)> = funded_accounts
.iter()
.map(|(eth_addr, balance)| {
let addr = Address::from(*eth_addr);
(address_to_account_id(addr), *balance)
})
.collect();

let token = TokenRef::initialize(metadata.clone(), balances, Some(minter), env)?.0;

let scheduler_acc = SchedulerRef::initialize(vec![], vec![], env)?.0;
scheduler_acc.update_begin_blockers(vec![], env)?;

Ok(EvdGenesisResult {
token: token.0,
scheduler: scheduler_acc.0,
})
})
.expect("genesis failed");

Expand All @@ -605,23 +560,6 @@ async fn run_custom_genesis<S: ReadonlyKV + Storage>(
}
}

fn do_custom_genesis(
metadata: evolve_fungible_asset::FungibleAssetMetadata,
balances: Vec<(AccountId, u128)>,
minter: AccountId,
env: &mut dyn Environment,
) -> SdkResult<EvdGenesisResult> {
let token = TokenRef::initialize(metadata, balances, Some(minter), env)?.0;

let scheduler_acc = SchedulerRef::initialize(vec![], vec![], env)?.0;
scheduler_acc.update_begin_blockers(vec![], env)?;

Ok(EvdGenesisResult {
token: token.0,
scheduler: scheduler_acc.0,
})
}

fn compute_block_hash(height: u64, timestamp: u64, parent_hash: B256) -> B256 {
let mut data = Vec::with_capacity(48);
data.extend_from_slice(&height.to_le_bytes());
Expand Down
7 changes: 6 additions & 1 deletion bin/testapp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ evolve_storage.workspace = true
evolve_node.workspace = true
evolve_mempool.workspace = true
evolve_tx_eth.workspace = true
evolve_grpc.workspace = true
evolve_chain_index.workspace = true
evolve_eth_jsonrpc.workspace = true
evolve_rpc_types.workspace = true
alloy-primitives.workspace = true
alloy-consensus = { workspace = true, features = ["k256"] }
k256 = { version = "0.13", features = ["ecdsa", "arithmetic"] }
rand = "0.8"
tiny-keccak = { version = "2.0", features = ["keccak"] }

borsh.workspace = true
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
tokio.workspace = true
Expand All @@ -48,7 +54,6 @@ commonware-runtime.workspace = true
[dev-dependencies]
tempfile = "3.8"
criterion = "0.5"
serde_json = "1.0"
alloy-primitives = { workspace = true }
async-trait = { workspace = true }
evolve_mempool = { workspace = true }
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions bin/testapp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod eth_eoa;
pub mod genesis_config;
pub mod sim_testing;

use crate::eth_eoa::eth_eoa_account::{EthEoaAccount, EthEoaAccountRef};
Expand Down
Loading
Loading