Skip to content

Commit 271d405

Browse files
committed
Add Docker Compose stack for evd and update branch changes
1 parent 89ff733 commit 271d405

14 files changed

Lines changed: 289 additions & 108 deletions

File tree

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.git
2+
.github
3+
.claude
4+
target
5+
docs/node_modules
6+
docs/.next
7+
docs/dist
8+
data
9+
.data

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,27 @@ just node-run
3939

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

42+
### Run `evd` (and optionally `ev-node`) with Docker Compose
43+
44+
Run `evd` only:
45+
46+
```bash
47+
docker compose up --build evd
48+
```
49+
50+
Run `evd` + `ev-node`:
51+
52+
```bash
53+
EV_NODE_IMAGE=<your-ev-node-image> \
54+
docker compose -f docker-compose.yml -f docker-compose.ev-node.yml up --build
55+
```
56+
57+
Notes:
58+
- `evd` JSON-RPC is exposed on `http://localhost:8545`
59+
- `evd` gRPC is exposed on `localhost:50051`
60+
- `ev-node` gets `EVD_GRPC_ENDPOINT=evd:50051` in the compose network
61+
- if your `ev-node` image needs explicit startup flags, override `command` for the `ev-node` service with an extra compose override file
62+
4263
## Documentation
4364

4465
Read the docs for implementation details instead of this README.

bin/evd/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ evolve_evnode = { workspace = true, features = ["testapp"] }
2424
evolve_testapp.workspace = true
2525
evolve_token.workspace = true
2626
evolve_scheduler.workspace = true
27-
evolve_fungible_asset.workspace = true
2827
evolve_testing.workspace = true
2928
evolve_tx_eth.workspace = true
3029
evolve_chain_index.workspace = true
@@ -42,8 +41,6 @@ tokio.workspace = true
4241
tracing.workspace = true
4342
tracing-subscriber.workspace = true
4443
borsh.workspace = true
45-
serde = { version = "1.0", features = ["derive"] }
46-
serde_json = "1.0"
4744

4845
[lints]
4946
workspace = true

bin/evd/src/main.rs

Lines changed: 34 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@
6262
//! }
6363
//! ```
6464
65-
mod genesis_config;
66-
6765
use std::sync::atomic::{AtomicU64, Ordering};
6866
use std::sync::Arc;
6967
use std::time::Duration;
@@ -76,8 +74,7 @@ use evolve_chain_index::{
7674
build_index_data, BlockMetadata, ChainIndex, ChainStateProvider, ChainStateProviderConfig,
7775
PersistentChainIndex,
7876
};
79-
use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX;
80-
use evolve_core::{AccountId, Environment, Message, ReadonlyKV, SdkResult};
77+
use evolve_core::{AccountId, ReadonlyKV};
8178
use evolve_eth_jsonrpc::{start_server_with_subscriptions, RpcServerConfig, SubscriptionManager};
8279
use evolve_evnode::{EvnodeServer, EvnodeServerConfig, ExecutorServiceConfig, OnBlockExecuted};
8380
use evolve_mempool::{new_shared_mempool, Mempool, SharedMempool};
@@ -92,6 +89,7 @@ use evolve_server::{
9289
};
9390
use evolve_stf_traits::{AccountsCodeStorage, StateChange};
9491
use evolve_storage::{Operation, QmdbStorage, Storage, StorageConfig};
92+
use evolve_testapp::genesis_config::{EvdGenesisConfig, EvdGenesisResult};
9593
use evolve_testapp::{
9694
build_mempool_stf, default_gas_config, do_genesis_inner, install_account_codes,
9795
PLACEHOLDER_ACCOUNT,
@@ -101,8 +99,6 @@ use evolve_token::account::TokenRef;
10199
use evolve_tx_eth::address_to_account_id;
102100
use evolve_tx_eth::TxContext;
103101

104-
use genesis_config::{EvdGenesisConfig, EvdGenesisResult};
105-
106102
#[derive(Parser)]
107103
#[command(name = "evd")]
108104
#[command(about = "Evolve node daemon with gRPC execution layer")]
@@ -201,7 +197,7 @@ fn run_node(config: NodeConfig, genesis_config: Option<EvdGenesisConfig>) {
201197
}
202198
None => {
203199
tracing::info!("No existing state found, running genesis...");
204-
let output = run_genesis(&storage, &codes, genesis_config.as_ref()).await;
200+
let output = run_genesis(&storage, &codes, genesis_config.as_ref());
205201
commit_genesis(&storage, output.changes, &output.genesis_result)
206202
.await
207203
.expect("genesis commit failed");
@@ -439,7 +435,7 @@ fn init_genesis(data_dir: &str, genesis_config: Option<EvdGenesisConfig>) {
439435
}
440436

441437
let codes = build_codes();
442-
let output = run_genesis(&storage, &codes, genesis_config.as_ref()).await;
438+
let output = run_genesis(&storage, &codes, genesis_config.as_ref());
443439

444440
commit_genesis(&storage, output.changes, &output.genesis_result)
445441
.await
@@ -457,48 +453,14 @@ fn build_codes() -> AccountStorageMock {
457453
codes
458454
}
459455

460-
/// Pre-register an EOA account in storage so genesis can reference it.
461-
fn build_eoa_registration(account_id: AccountId, eth_address: [u8; 20]) -> Vec<Operation> {
462-
let mut ops = Vec::with_capacity(3);
463-
464-
// 1. Register account code identifier
465-
let mut key = vec![ACCOUNT_IDENTIFIER_PREFIX];
466-
key.extend_from_slice(&account_id.as_bytes());
467-
let value = Message::new(&"EthEoaAccount".to_string())
468-
.unwrap()
469-
.into_bytes()
470-
.unwrap();
471-
ops.push(Operation::Set { key, value });
472-
473-
// 2. Set nonce = 0 (Item prefix 0)
474-
let mut nonce_key = account_id.as_bytes().to_vec();
475-
nonce_key.push(0u8);
476-
let nonce_value = Message::new(&0u64).unwrap().into_bytes().unwrap();
477-
ops.push(Operation::Set {
478-
key: nonce_key,
479-
value: nonce_value,
480-
});
481-
482-
// 3. Set eth_address (Item prefix 1)
483-
let mut addr_key = account_id.as_bytes().to_vec();
484-
addr_key.push(1u8);
485-
let addr_value = Message::new(&eth_address).unwrap().into_bytes().unwrap();
486-
ops.push(Operation::Set {
487-
key: addr_key,
488-
value: addr_value,
489-
});
490-
491-
ops
492-
}
493-
494456
/// Run genesis using the default testapp genesis or a custom genesis config.
495-
async fn run_genesis<S: ReadonlyKV + Storage>(
457+
fn run_genesis<S: ReadonlyKV + Storage>(
496458
storage: &S,
497459
codes: &AccountStorageMock,
498460
genesis_config: Option<&EvdGenesisConfig>,
499461
) -> GenesisOutput<EvdGenesisResult> {
500462
match genesis_config {
501-
Some(config) => run_custom_genesis(storage, codes, config).await,
463+
Some(config) => run_custom_genesis(storage, codes, config),
502464
None => run_default_genesis(storage, codes),
503465
}
504466
}
@@ -534,56 +496,29 @@ fn run_default_genesis<S: ReadonlyKV + Storage>(
534496
}
535497

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

544-
// Parse accounts that have a non-zero balance (need pre-registration for genesis funding).
545-
// Other accounts are auto-registered by the STF on their first transaction.
546-
let funded_accounts: Vec<(Address, u128)> = genesis_config
510+
let funded_accounts: Vec<([u8; 20], u128)> = genesis_config
547511
.accounts
548512
.iter()
549513
.filter(|acc| acc.balance > 0)
550514
.map(|acc| {
551515
let addr = acc
552516
.parse_address()
553517
.expect("invalid address in genesis config");
554-
(addr, acc.balance)
518+
(addr.into_array(), acc.balance)
555519
})
556520
.collect();
557521

558-
// Pre-register only funded EOA accounts in storage
559-
let mut pre_ops = Vec::new();
560-
for (addr, _) in &funded_accounts {
561-
let id = address_to_account_id(*addr);
562-
let addr_bytes: [u8; 20] = addr.into_array();
563-
pre_ops.extend(build_eoa_registration(id, addr_bytes));
564-
}
565-
566-
storage
567-
.batch(pre_ops)
568-
.await
569-
.expect("pre-register EOAs failed");
570-
storage.commit().await.expect("pre-register commit failed");
571-
572-
tracing::info!(
573-
"Pre-registered {} funded EOA accounts:",
574-
funded_accounts.len()
575-
);
576-
for (i, (addr, balance)) in funded_accounts.iter().enumerate() {
577-
let id = address_to_account_id(*addr);
578-
tracing::info!(" #{:02}: {:?} (0x{:x}) balance={}", i, id, addr, balance);
579-
}
580-
581-
// Build balances list for genesis token initialization
582-
let balances: Vec<(AccountId, u128)> = funded_accounts
583-
.iter()
584-
.map(|(addr, balance)| (address_to_account_id(*addr), *balance))
585-
.collect();
586-
587522
let minter = AccountId::new(genesis_config.minter_id);
588523
let metadata = genesis_config.token.to_metadata();
589524

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

594529
let (genesis_result, state) = stf
595530
.system_exec(storage, codes, genesis_block, |env| {
596-
do_custom_genesis(metadata.clone(), balances.clone(), minter, env)
531+
for (eth_addr, _) in &funded_accounts {
532+
EthEoaAccountRef::initialize(*eth_addr, env)?;
533+
}
534+
535+
let balances: Vec<(AccountId, u128)> = funded_accounts
536+
.iter()
537+
.map(|(eth_addr, balance)| {
538+
let addr = Address::from(*eth_addr);
539+
(address_to_account_id(addr), *balance)
540+
})
541+
.collect();
542+
543+
let token = TokenRef::initialize(metadata.clone(), balances, Some(minter), env)?.0;
544+
545+
let scheduler_acc = SchedulerRef::initialize(vec![], vec![], env)?.0;
546+
scheduler_acc.update_begin_blockers(vec![], env)?;
547+
548+
Ok(EvdGenesisResult {
549+
token: token.0,
550+
scheduler: scheduler_acc.0,
551+
})
597552
})
598553
.expect("genesis failed");
599554

@@ -605,23 +560,6 @@ async fn run_custom_genesis<S: ReadonlyKV + Storage>(
605560
}
606561
}
607562

608-
fn do_custom_genesis(
609-
metadata: evolve_fungible_asset::FungibleAssetMetadata,
610-
balances: Vec<(AccountId, u128)>,
611-
minter: AccountId,
612-
env: &mut dyn Environment,
613-
) -> SdkResult<EvdGenesisResult> {
614-
let token = TokenRef::initialize(metadata, balances, Some(minter), env)?.0;
615-
616-
let scheduler_acc = SchedulerRef::initialize(vec![], vec![], env)?.0;
617-
scheduler_acc.update_begin_blockers(vec![], env)?;
618-
619-
Ok(EvdGenesisResult {
620-
token: token.0,
621-
scheduler: scheduler_acc.0,
622-
})
623-
}
624-
625563
fn compute_block_hash(height: u64, timestamp: u64, parent_hash: B256) -> B256 {
626564
let mut data = Vec::with_capacity(48);
627565
data.extend_from_slice(&height.to_le_bytes());

bin/testapp/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ evolve_storage.workspace = true
2323
evolve_node.workspace = true
2424
evolve_mempool.workspace = true
2525
evolve_tx_eth.workspace = true
26+
evolve_grpc.workspace = true
27+
evolve_chain_index.workspace = true
28+
evolve_eth_jsonrpc.workspace = true
29+
evolve_rpc_types.workspace = true
2630
alloy-primitives.workspace = true
2731
alloy-consensus = { workspace = true, features = ["k256"] }
2832
k256 = { version = "0.13", features = ["ecdsa", "arithmetic"] }
2933
rand = "0.8"
3034
tiny-keccak = { version = "2.0", features = ["keccak"] }
3135

3236
borsh.workspace = true
37+
serde = { version = "1.0", features = ["derive"] }
38+
serde_json = "1.0"
3339
tracing = { workspace = true }
3440
tracing-subscriber = { workspace = true }
3541
tokio.workspace = true
@@ -48,7 +54,6 @@ commonware-runtime.workspace = true
4854
[dev-dependencies]
4955
tempfile = "3.8"
5056
criterion = "0.5"
51-
serde_json = "1.0"
5257
alloy-primitives = { workspace = true }
5358
async-trait = { workspace = true }
5459
evolve_mempool = { workspace = true }

bin/testapp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod eth_eoa;
2+
pub mod genesis_config;
23
pub mod sim_testing;
34

45
use crate::eth_eoa::eth_eoa_account::{EthEoaAccount, EthEoaAccountRef};

0 commit comments

Comments
 (0)