Skip to content

Commit

Permalink
feat: Minor extensions for server interoperability (#21)
Browse files Browse the repository at this point in the history
# What ❔

- Simplifies generating executor configurations.
- Adds byte conversions for `BlockHeaderHash`.
- Makes `SyncBlocks` actor terminate successfully on context
cancellation.

## Why ❔

Part of preparations for the integration with the server codebase.
  • Loading branch information
slowli authored Nov 7, 2023
1 parent ccc3ee9 commit aaa1093
Show file tree
Hide file tree
Showing 21 changed files with 228 additions and 230 deletions.
67 changes: 0 additions & 67 deletions node/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ ark-ec = "0.4.2"
ark-serialize = { version = "0.4.2", features = ["std"] }
num-traits = "0.2.17"
clap = { version = "4.3.3", features = ["derive"] }
ed25519-dalek = { version = "2.0.0", features = ["serde", "rand_core"] }
futures = "0.3.28"
ed25519-dalek = { version = "2.0.0", features = ["rand_core"] }
hex = "0.4.3"
hyper = { version = "0.14.27", features = ["http1", "http2", "server", "tcp"] }
im = "15.1.0"
once_cell = "1.17.1"
pin-project = "1.1.0"
Expand Down
2 changes: 1 addition & 1 deletion node/actors/executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ license.workspace = true

[dependencies]
anyhow.workspace = true
rand.workspace = true
tracing.workspace = true
vise.workspace = true

Expand All @@ -23,5 +24,4 @@ network = { path = "../network" }
sync_blocks = { path = "../sync_blocks" }

[dev-dependencies]
rand.workspace = true
tokio.workspace = true
1 change: 1 addition & 0 deletions node/actors/executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use utils::pipe;
mod config;
mod io;
mod metrics;
pub mod testonly;
#[cfg(test)]
mod tests;

Expand Down
99 changes: 99 additions & 0 deletions node/actors/executor/src/testonly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Testing extensions for node executor.
use crate::config::{ConsensusConfig, ExecutorConfig, GossipConfig};
use concurrency::net;
use consensus::testonly::make_genesis;
use network::testonly::Instance;
use rand::Rng;
use roles::{
node,
validator::{self, Payload},
};
use std::collections::HashMap;

/// Full validator configuration.
#[derive(Debug)]
#[non_exhaustive]
pub struct FullValidatorConfig {
/// Executor configuration.
pub node_config: ExecutorConfig,
/// Secret key of the node used for identification in the gossip network.
pub node_key: node::SecretKey,
/// Consensus configuration of the validator.
pub consensus_config: ConsensusConfig,
/// Secret key for consensus.
pub validator_key: validator::SecretKey,
}

impl FullValidatorConfig {
/// Generates a validator config for a network with a single validator.
pub fn for_single_validator(rng: &mut impl Rng, genesis_block_payload: Payload) -> Self {
let mut net_configs = Instance::new_configs(rng, 1, 0);
assert_eq!(net_configs.len(), 1);
let net_config = net_configs.pop().unwrap();
let consensus_config = net_config.consensus.unwrap();
let validator_key = consensus_config.key.clone();
let consensus_config = ConsensusConfig::from(consensus_config);

let (genesis_block, validators) =
make_genesis(&[validator_key.clone()], genesis_block_payload);
let node_key = net_config.gossip.key.clone();
let node_config = ExecutorConfig {
server_addr: *net_config.server_addr,
gossip: net_config.gossip.into(),
genesis_block,
validators,
};

Self {
node_config,
node_key,
consensus_config,
validator_key,
}
}

/// Creates a new external node and configures this validator to accept incoming connections from it.
pub fn connect_external_node(&mut self, rng: &mut impl Rng) -> ExternalNodeConfig {
let external_node_config = ExternalNodeConfig::new(rng, self);
self.node_config
.gossip
.static_inbound
.insert(external_node_config.node_key.public());
external_node_config
}
}

/// Configuration for an external node (i.e., non-validator node).
#[derive(Debug)]
#[non_exhaustive]
pub struct ExternalNodeConfig {
/// Executor configuration.
pub node_config: ExecutorConfig,
/// Secret key of the node used for identification in the gossip network.
pub node_key: node::SecretKey,
}

impl ExternalNodeConfig {
fn new(rng: &mut impl Rng, validator: &FullValidatorConfig) -> Self {
let node_key: node::SecretKey = rng.gen();
let external_node_addr = net::tcp::testonly::reserve_listener();
let node_config = ExecutorConfig {
server_addr: *external_node_addr,
gossip: GossipConfig {
key: node_key.public(),
static_outbound: HashMap::from([(
validator.node_key.public(),
validator.node_config.server_addr,
)]),
..validator.node_config.gossip.clone()
},
..validator.node_config.clone()
};

Self {
node_config,
node_key,
}
}
}
84 changes: 9 additions & 75 deletions node/actors/executor/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
//! High-level tests for `Executor`.
use super::*;
use crate::testonly::FullValidatorConfig;
use concurrency::sync;
use consensus::testonly::make_genesis;
use network::testonly::Instance;
use rand::Rng;
use roles::validator::{BlockNumber, Payload};
use std::collections::HashMap;
use storage::{BlockStore, InMemoryStorage, StorageError};

async fn run_executor(ctx: &ctx::Ctx, executor: Executor<InMemoryStorage>) -> anyhow::Result<()> {
executor.run(ctx).await.or_else(|err| {
if err.root_cause().is::<ctx::Canceled>() {
Ok(()) // Test has successfully finished
} else {
Err(err)
}
})
}

async fn store_final_blocks(
ctx: &ctx::Ctx,
mut blocks_receiver: channel::UnboundedReceiver<FinalBlock>,
Expand All @@ -37,40 +24,7 @@ async fn store_final_blocks(
Ok(())
}

#[derive(Debug)]
struct FullValidatorConfig {
node_config: ExecutorConfig,
node_key: node::SecretKey,
consensus_config: ConsensusConfig,
validator_key: validator::SecretKey,
}

impl FullValidatorConfig {
fn for_single_validator(rng: &mut impl Rng) -> Self {
let mut net_configs = Instance::new_configs(rng, 1, 0);
assert_eq!(net_configs.len(), 1);
let net_config = net_configs.pop().unwrap();
let consensus_config = net_config.consensus.unwrap();
let validator_key = consensus_config.key.clone();
let consensus_config = ConsensusConfig::from(consensus_config);

let (genesis_block, validators) = make_genesis(&[validator_key.clone()], Payload(vec![]));
let node_key = net_config.gossip.key.clone();
let node_config = ExecutorConfig {
server_addr: *net_config.server_addr,
gossip: net_config.gossip.into(),
genesis_block,
validators,
};

Self {
node_config,
node_key,
consensus_config,
validator_key,
}
}

fn into_executor(
self,
storage: Arc<InMemoryStorage>,
Expand Down Expand Up @@ -98,14 +52,14 @@ async fn executing_single_validator() {
let ctx = &ctx::root();
let rng = &mut ctx.rng();

let validator = FullValidatorConfig::for_single_validator(rng);
let validator = FullValidatorConfig::for_single_validator(rng, Payload(vec![]));
let genesis_block = &validator.node_config.genesis_block;
let storage = InMemoryStorage::new(genesis_block.clone());
let storage = Arc::new(storage);
let (executor, mut blocks_receiver) = validator.into_executor(storage);

scope::run!(ctx, |ctx, s| async {
s.spawn_bg(run_executor(ctx, executor));
s.spawn_bg(executor.run(ctx));

let mut expected_block_number = BlockNumber(1);
while expected_block_number < BlockNumber(5) {
Expand All @@ -126,28 +80,8 @@ async fn executing_validator_and_external_node() {
let ctx = &ctx::test_root(&ctx::AffineClock::new(20.0));
let rng = &mut ctx.rng();

let mut validator = FullValidatorConfig::for_single_validator(rng);

let external_node_key: node::SecretKey = rng.gen();
let external_node_addr = net::tcp::testonly::reserve_listener();
let external_node_config = ExecutorConfig {
server_addr: *external_node_addr,
gossip: GossipConfig {
key: external_node_key.public(),
static_outbound: HashMap::from([(
validator.node_key.public(),
validator.node_config.server_addr,
)]),
..validator.node_config.gossip.clone()
},
..validator.node_config.clone()
};

validator
.node_config
.gossip
.static_inbound
.insert(external_node_key.public());
let mut validator = FullValidatorConfig::for_single_validator(rng, Payload(vec![]));
let external_node = validator.connect_external_node(rng);

let genesis_block = &validator.node_config.genesis_block;
let validator_storage = InMemoryStorage::new(genesis_block.clone());
Expand All @@ -158,15 +92,15 @@ async fn executing_validator_and_external_node() {

let (validator, blocks_receiver) = validator.into_executor(validator_storage.clone());
let external_node = Executor::new(
external_node_config,
external_node_key,
external_node.node_config,
external_node.node_key,
external_node_storage.clone(),
)
.unwrap();

scope::run!(ctx, |ctx, s| async {
s.spawn_bg(run_executor(ctx, validator));
s.spawn_bg(run_executor(ctx, external_node));
s.spawn_bg(validator.run(ctx));
s.spawn_bg(external_node.run(ctx));
s.spawn_bg(store_final_blocks(ctx, blocks_receiver, validator_storage));

for _ in 0..5 {
Expand Down
1 change: 0 additions & 1 deletion node/actors/network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ schema = { path = "../../libs/schema" }
utils = { path = "../../libs/utils" }

[dev-dependencies]
futures.workspace = true
pretty_assertions.workspace = true
test-casing.workspace = true
tokio.workspace = true
Loading

0 comments on commit aaa1093

Please sign in to comment.