diff --git a/.dockerignore b/.dockerignore index f13190a32..67645b73a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,5 +6,6 @@ !crates !tests !Cargo.* +!tools/init.sh !LICENSE diff --git a/Dockerfile b/Dockerfile index 64a225fca..d0d9c082b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,7 +60,12 @@ RUN adduser \ WORKDIR /usr/src/app COPY --from=build /usr/src/app/target/release/topos . +COPY tools/init.sh ./init.sh + +RUN apt-get update && apt-get install jq -y USER topos:topos -ENTRYPOINT ["./topos"] +RUN mkdir /tmp/shared + +ENTRYPOINT ["./init.sh"] diff --git a/crates/topos-p2p/src/lib.rs b/crates/topos-p2p/src/lib.rs index aae3faab3..054eec5e9 100644 --- a/crates/topos-p2p/src/lib.rs +++ b/crates/topos-p2p/src/lib.rs @@ -34,4 +34,18 @@ pub mod utils { None => identity::Keypair::generate_ed25519(), } } + + pub fn local_key_pair_from_slice(slice: &[u8]) -> identity::Keypair { + // todo: load from protobuf encoded|base64 encoded config.local_key_pair + let mut bytes = [0u8; 32]; + if slice.len() <= 32 { + bytes[..slice.len()].clone_from_slice(slice); + } else { + bytes.clone_from_slice(&slice[..32]); + } + + let secret_key = identity::ed25519::SecretKey::from_bytes(&mut bytes) + .expect("this returns `Err` only if the length is wrong; the length is correct; qed"); + identity::Keypair::Ed25519(secret_key.into()) + } } diff --git a/crates/topos-tce-synchronizer/src/builder.rs b/crates/topos-tce-synchronizer/src/builder.rs index 513c100e9..a647734fd 100644 --- a/crates/topos-tce-synchronizer/src/builder.rs +++ b/crates/topos-tce-synchronizer/src/builder.rs @@ -3,6 +3,7 @@ use std::future::IntoFuture; use futures::{future::BoxFuture, FutureExt, TryFutureExt}; use tokio::{spawn, sync::mpsc}; use tokio_stream::wrappers::ReceiverStream; +use topos_p2p::Client as NetworkClient; use topos_tce_gatekeeper::GatekeeperClient; use crate::{ @@ -13,6 +14,7 @@ use crate::{ #[derive(Default)] pub struct SynchronizerBuilder { gatekeeper_client: Option, + network_client: Option, } impl IntoFuture for SynchronizerBuilder { @@ -34,6 +36,7 @@ impl IntoFuture for SynchronizerBuilder { CheckpointsCollector::builder() .set_gatekeeper_client(self.gatekeeper_client.take()) + .set_network_client(self.network_client.take()) .into_future() .map_err(Into::into) .and_then( @@ -66,4 +69,10 @@ impl SynchronizerBuilder { self } + + pub fn with_network_client(mut self, network_client: NetworkClient) -> Self { + self.network_client = Some(network_client); + + self + } } diff --git a/crates/topos-tce-synchronizer/src/checkpoints_collector/builder.rs b/crates/topos-tce-synchronizer/src/checkpoints_collector/builder.rs index f76259d9b..7d156fe35 100644 --- a/crates/topos-tce-synchronizer/src/checkpoints_collector/builder.rs +++ b/crates/topos-tce-synchronizer/src/checkpoints_collector/builder.rs @@ -23,6 +23,12 @@ impl CheckpointsCollectorBuilder { self } + + pub fn set_network_client(mut self, network_client: Option) -> Self { + self.network_client = network_client; + + self + } } impl IntoFuture for CheckpointsCollectorBuilder { diff --git a/crates/topos-tce/src/lib.rs b/crates/topos-tce/src/lib.rs index 40e35f400..ad02c3256 100644 --- a/crates/topos-tce/src/lib.rs +++ b/crates/topos-tce/src/lib.rs @@ -10,6 +10,7 @@ use opentelemetry::sdk::Resource; use opentelemetry::{global, KeyValue}; use tce_transport::ReliableBroadcastParams; use tokio::spawn; +use topos_p2p::utils::local_key_pair_from_slice; use topos_p2p::{utils::local_key_pair, Multiaddr, PeerId}; use topos_tce_broadcast::{ReliableBroadcastClient, ReliableBroadcastConfig}; use topos_tce_storage::{Connection, RocksDBStorage}; @@ -18,7 +19,7 @@ use tracing_subscriber::{prelude::*, EnvFilter}; #[derive(Debug)] pub struct TceConfiguration { - pub local_key_seed: Option, + pub local_key_seed: Option>, pub jaeger_agent: String, pub jaeger_service_name: String, pub tce_params: ReliableBroadcastParams, @@ -36,7 +37,12 @@ pub enum StorageConfiguration { #[instrument(name = "TCE", fields(peer_id), skip(config))] pub async fn run(config: &TceConfiguration) -> Result<(), Box> { - let key = local_key_pair(config.local_key_seed); + let key = if let Some(seed) = &config.local_key_seed { + local_key_pair_from_slice(seed) + } else { + local_key_pair(None) + }; + let peer_id = key.public().to_peer_id(); tracing::Span::current().record("peer_id", &peer_id.to_string()); @@ -132,6 +138,7 @@ pub async fn run(config: &TceConfiguration) -> Result<(), Box), } diff --git a/crates/topos/src/components/tce/commands/peer_id.rs b/crates/topos/src/components/tce/commands/peer_id.rs new file mode 100644 index 000000000..95cdc8daf --- /dev/null +++ b/crates/topos/src/components/tce/commands/peer_id.rs @@ -0,0 +1,7 @@ +use clap::Args; + +#[derive(Args, Debug)] +pub(crate) struct PeerId { + #[arg(long = "from-slice")] + pub(crate) from_slice: Option, +} diff --git a/crates/topos/src/components/tce/commands/run.rs b/crates/topos/src/components/tce/commands/run.rs index 6e8a9abc4..891b682a6 100644 --- a/crates/topos/src/components/tce/commands/run.rs +++ b/crates/topos/src/components/tce/commands/run.rs @@ -31,7 +31,7 @@ pub struct Run { /// Local peer secret key seed (optional, used for testing) #[clap(long, env = "TCE_LOCAL_KS")] - pub local_key_seed: Option, + pub local_key_seed: Option, /// Local peer key-pair (in base64 format) #[clap(long, env = "TCE_LOCAL_KEYPAIR")] diff --git a/crates/topos/src/components/tce/mod.rs b/crates/topos/src/components/tce/mod.rs index 5d18a01ee..3d430e479 100644 --- a/crates/topos/src/components/tce/mod.rs +++ b/crates/topos/src/components/tce/mod.rs @@ -85,7 +85,7 @@ pub(crate) async fn handle_command( Some(TceCommands::Run(cmd)) => { let config = TceConfiguration { boot_peers: cmd.parse_boot_peers(), - local_key_seed: cmd.local_key_seed, + local_key_seed: cmd.local_key_seed.map(|s| s.as_bytes().to_vec()), jaeger_agent: cmd.jaeger_agent, jaeger_service_name: cmd.jaeger_service_name, tce_local_port: cmd.tce_local_port, @@ -115,6 +115,21 @@ pub(crate) async fn handle_command( Ok(()) } + + Some(TceCommands::PeerId(cmd)) => { + match cmd.from_slice { + Some(slice) => println!( + "{}", + topos_p2p::utils::local_key_pair_from_slice(slice.as_bytes()) + .public() + .to_peer_id() + ), + _ => {} + }; + + Ok(()) + } + None => Ok(()), } } diff --git a/crates/topos/tests/snapshots/tce__can_get_a_peer_id_from_a_seed.snap b/crates/topos/tests/snapshots/tce__can_get_a_peer_id_from_a_seed.snap new file mode 100644 index 000000000..7bdbd0580 --- /dev/null +++ b/crates/topos/tests/snapshots/tce__can_get_a_peer_id_from_a_seed.snap @@ -0,0 +1,6 @@ +--- +source: crates/topos/tests/tce.rs +expression: result +--- +12D3KooWRhFCXBhmsMnur3up3vJsDoqWh4c39PKXgSWwzAzDHNLn + diff --git a/crates/topos/tests/tce.rs b/crates/topos/tests/tce.rs index a12aafd1e..a3c157a28 100644 --- a/crates/topos/tests/tce.rs +++ b/crates/topos/tests/tce.rs @@ -55,6 +55,20 @@ async fn do_not_push_empty_list() -> Result<(), Box> { Ok(()) } +#[tokio::test] +async fn can_get_a_peer_id_from_a_seed() -> Result<(), Box> { + let mut cmd = Command::cargo_bin("topos")?; + cmd.arg("tce").arg("peer-id").arg("--from-slice").arg("1"); + + let output = cmd.assert().success(); + + let result: &str = std::str::from_utf8(&output.get_output().stdout)?; + + insta::assert_snapshot!(result); + + Ok(()) +} + struct DummyServer; #[tonic::async_trait] diff --git a/tools/docker-compose.yml b/tools/docker-compose.yml index c682b8871..906f9be0a 100644 --- a/tools/docker-compose.yml +++ b/tools/docker-compose.yml @@ -2,7 +2,6 @@ services: jaeger: image: jaegertracing/all-in-one:latest container_name: jaeger - restart: always ports: - "6831/udp" - "6832/udp" @@ -10,9 +9,11 @@ services: - "16686:16686" boot: container_name: boot - command: tce run -vv + command: boot tce run -vv image: ghcr.io/toposware/tce:pr-4 init: true + volumes: + - shared:/tmp/shared build: context: ../ args: @@ -27,8 +28,9 @@ services: - RUST_LOG=info,topos_p2p=debug,topos_tce_node_app=debug,topos_tce_protocols_reliable_broadcast=debug - TOOLCHAIN_VERSION=stable - RUST_BACKTRACE=full + - TCE_DB_PATH=/tmp/default-db - TCE_API_ADDR=0.0.0.0:1340 - - TCE_LOCAL_KS=1 # 12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X + - TCE_LOCAL_KS=1 # 12D3KooWRhFCXBhmsMnur3up3vJsDoqWh4c39PKXgSWwzAzDHNLn - TCE_JAEGER_SERVICE_NAME=tce-boot-node - TCE_JAEGER_AGENT=jaeger:6831 - TCE_ECHO_SAMPLE_SIZE=3 @@ -42,6 +44,8 @@ services: image: ghcr.io/toposware/tce:pr-4 command: tce run init: true + volumes: + - shared:/tmp/shared build: context: ../ args: @@ -56,11 +60,13 @@ services: deploy: replicas: 5 environment: + - LOCAL_TEST_NET=true - RUST_LOG=info,topos_p2p=debug,topos_tce_node_app=debug,topos_tce_protocols_reliable_broadcast=debug + - TCE_DB_PATH=/tmp/default-db - TCE_API_ADDR=0.0.0.0:1340 - TCE_JAEGER_SERVICE_NAME=tce-regular-node - TCE_JAEGER_AGENT=jaeger:6831 - - TCE_BOOT_PEERS=12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X /dns4/boot/tcp/9090 + - TCE_BOOT_PEERS=12D3KooWRhFCXBhmsMnur3up3vJsDoqWh4c39PKXgSWwzAzDHNLn /dns4/boot/tcp/9090 - TCE_ECHO_SAMPLE_SIZE=3 - TCE_READY_SAMPLE_SIZE=3 - TCE_DELIVERY_SAMPLE_SIZE=3 @@ -72,11 +78,14 @@ services: container_name: spam image: ghcr.io/toposware/cert-spammer:main init: true + volumes: + - shared:/tmp/shared environment: - - RUST_LOG=debug - - TARGET_NODES_PATH=/nodes.json + - RUST_LOG=info + - TARGET_NODES_PATH=/tmp/shared/peer_nodes.json depends_on: - jaeger - peer - volumes: - - ./peer_nodes.json:/nodes.json + +volumes: + shared: diff --git a/tools/init.sh b/tools/init.sh new file mode 100755 index 000000000..1b29e0fac --- /dev/null +++ b/tools/init.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +set -e + +if ! command -v jq &> /dev/null +then + echo "jq could not be found" + exit +fi + +JQ=$(which jq) +TOPOS_BIN=./topos +PEER_LIST_PATH=/tmp/shared/peer_ids.json +NODE_LIST_PATH=/tmp/shared/peer_nodes.json +NODE="http://$HOSTNAME:1340" + +case "$1" in + + "boot") + echo "Generating peer list file..." + $JQ -n --arg PEER $($TOPOS_BIN tce peer-id --from-slice=$TCE_LOCAL_KS) '[$PEER]' > $PEER_LIST_PATH + echo "Peer list file have been successfully generated" + + echo "Generating node list file..." + $JQ -n --arg NODE $NODE '{"nodes": [$NODE]}' > $NODE_LIST_PATH + echo "Peer nodes list have been successfully generated" + + echo "Starting boot node..." + exec "$TOPOS_BIN" "${@:2}" + ;; + + *) + if [[ ! -z ${LOCAL_TEST_NET+x} ]]; then + until [ -f "$PEER_LIST_PATH" ] + do + echo "Waiting 1s for peer_list file $PEER_LIST_PATH to be created by boot container..." + sleep 1 + done + + PEER=$($TOPOS_BIN tce peer-id --from-slice=$HOSTNAME) + cat <<< $($JQ --arg PEER $PEER '. += [$PEER]' $PEER_LIST_PATH) > $PEER_LIST_PATH + + export TCE_LOCAL_KS=$HOSTNAME + + until [ -f "$NODE_LIST_PATH" ] + do + echo "Waiting 1s for node_list file $NODE_LIST_PATH to be created by boot container..." + sleep 1 + done + + cat <<< $($JQ --arg NODE $NODE '.nodes += [$NODE]' $NODE_LIST_PATH) > $NODE_LIST_PATH + fi + + exec "$TOPOS_BIN" "$@" + ;; + +esac