Skip to content

Commit

Permalink
Merge pull request #35 from sideprotocol/internal/latency-test
Browse files Browse the repository at this point in the history
Add a whitelist of peers to safeguard against spam attacks.
  • Loading branch information
lazyluis authored Nov 15, 2024
2 parents 774bddf + 5f9427e commit 716ea88
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 89 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shuttler"
version = "0.4.1"
version = "0.5.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
60 changes: 33 additions & 27 deletions src/app/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ use cosmos_sdk_proto::cosmos::auth::v1beta1::{query_client::QueryClient as AuthQ

use frost_secp256k1_tr::keys::{KeyPackage, PublicKeyPackage};
use serde::{Deserialize, Serialize};
use tracing::error;
use tendermint_config::PrivValidatorKey;
use std::{fs, path::PathBuf, str::FromStr, sync::Mutex, time::Duration};

use crate::helper::{cipher::random_bytes, encoding::to_base64};
use crate::helper::cipher::random_bytes;

const CONFIG_FILE: &str = "config.toml";

Expand All @@ -26,7 +25,7 @@ lazy_static! {
pub struct Config {
#[serde(skip_serializing, skip_deserializing)]
pub home: PathBuf,
pub p2p_keypair: String,
// pub p2p_keypair: String,
pub port: u32,
pub bootstrap_nodes: Vec<String>,
/// logger level
Expand Down Expand Up @@ -104,13 +103,6 @@ pub struct AnyKey {
pub value: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PrivValidatorKey {
pub address: String,
pub pub_key: AnyKey,
pub priv_key: AnyKey,
}

/// relayer account will be used to sign transactions on the side chain,
/// such as sending block headers, depositing and withdrawing transactions
pub async fn get_relayer_account(conf: &Config) -> BaseAccount {
Expand Down Expand Up @@ -154,24 +146,23 @@ pub fn remove_relayer_account() {
}

impl Config {
pub fn load_validator_key(&self) {
pub fn load_validator_key(&self) -> PrivValidatorKey {
let priv_key_path = if self.priv_validator_key_path.starts_with("/") {
PathBuf::from(self.priv_validator_key_path.clone())
} else {
self.home.join(self.priv_validator_key_path.clone())
};
match fs::read_to_string(priv_key_path.clone()) {
Ok(text) => {
let prv_key = serde_json::from_str::<PrivValidatorKey>(text.as_str()).expect("Failed to parse priv_validator_key.json");
PRIV_VALIDATOR_KEY.lock().unwrap().replace(prv_key.clone());
},
Err(e) => error!("You are running in Bootstrap mode because the 'priv_validator_key.json' has not been loaded: {}", e)
};
let text = fs::read_to_string(priv_key_path.clone()).expect("priv_validator_key.json does not exists!");

let prv_key = serde_json::from_str::<PrivValidatorKey>(text.as_str()).expect("Failed to parse priv_validator_key.json");
// PRIV_VALIDATOR_KEY.lock().unwrap().replace(prv_key.clone());
prv_key

}

pub fn get_validator_key(&self) -> Option<PrivValidatorKey> {
PRIV_VALIDATOR_KEY.lock().unwrap().clone()
}
// pub fn get_validator_key(&self) -> Option<PrivValidatorKey> {
// PRIV_VALIDATOR_KEY.lock().unwrap().clone()
// }

pub fn from_file(app_home: &str) -> Result<Self, std::io::Error> {

Expand All @@ -193,25 +184,40 @@ impl Config {
Ok(config)
}

pub fn generate_priv_validator_key(home: PathBuf) {
let rng = rand::thread_rng();
let sk = ed25519_consensus::SigningKey::new(rng);
let priv_key = tendermint::private_key::PrivateKey::from_ed25519_consensus(sk);

let key = tendermint_config::PrivValidatorKey {
address: tendermint::account::Id::from(priv_key.public_key()),
pub_key: priv_key.public_key(),
priv_key,
};

fs::create_dir_all(&home).unwrap();
let text= serde_json::to_string_pretty(&key).unwrap();
fs::write(home.join("priv_validator_key.json"), text).unwrap();
}

pub fn default(home_str: &str, port: u32, network: Network) -> Self {
let entropy = random_bytes(32);
let mnemonic = bip39::Mnemonic::from_entropy(entropy.as_slice()).expect("failed to create mnemonic");
let p2p_keypair = to_base64(libp2p::identity::Keypair::generate_ed25519().to_protobuf_encoding().unwrap().as_slice());
// let p2p_keypair = to_base64(libp2p::identity::Keypair::generate_ed25519().to_protobuf_encoding().unwrap().as_slice());
let home = if home_str.starts_with("/") {
PathBuf::from_str(home_str).unwrap()
} else {
home_dir(home_str)
};
Self::generate_priv_validator_key(home.clone());
Self {
home,
p2p_keypair ,
// p2p_keypair ,
port: port as u32,
bootstrap_nodes: vec!["/ip4/192.248.180.245/tcp/5158/p2p/12D3KooWMpMtmYQKSn1sZaSRn4CAcsraWZVrZ2zdNjEgsEPSd3Pv".to_string()],
bootstrap_nodes: vec![],
log_level: "debug".to_string(),
mnemonic: mnemonic.to_string(),
priv_validator_key_path: "priv_validator_key.json".to_string(),
// keys: BTreeMap::new(),
// pubkeys: BTreeMap::new(),
bitcoin: BitcoinCfg {
network,
rpc: "http://192.248.150.102:18332".to_string(),
Expand Down
86 changes: 62 additions & 24 deletions src/app/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ use libp2p::identity::Keypair;
use libp2p::kad::store::MemoryStore;

use libp2p::swarm::SwarmEvent;
use libp2p::{ gossipsub, identify, mdns, noise, tcp, yamux, Multiaddr, PeerId, Swarm};
use libp2p::{ gossipsub, identify, kad, mdns, noise, tcp, yamux, Multiaddr, PeerId, Swarm};
use serde::Serialize;

use crate::app::config::{self, TASK_INTERVAL};
use crate::app::config::Config;
use crate::helper::bitcoin::get_group_address_by_tweak;
use crate::helper::cipher::random_bytes;
use crate::helper::encoding::from_base64;
use crate::helper::encoding::identifier_to_peer_id;
use crate::helper::gossip::{subscribe_gossip_topics, HeartBeatMessage, SubscribeTopic};
use crate::helper::mem_store;
use crate::protocols::sign::{received_sign_message, SignMesage, SignTask};
Expand Down Expand Up @@ -66,19 +65,20 @@ pub struct Signer {
db_dkg: sled::Db,
db_dkg_variables: sled::Db,
db_keypair: sled::Db,

}

impl Signer {
pub fn new(conf: Config) -> Self {

// load private key from priv_validator_key_path
let local_key = match conf.get_validator_key() {
Some(validator_key) => {
let b = from_base64(&validator_key.priv_key.value).expect("Decode private key failed");
SecretKey::from_slice(b.as_slice()).expect("invalid secret key")
},
None => SecretKey::from_slice(random_bytes(SecretKey::BYTES).as_slice()).expect("invalid secret key")
};
let priv_validator_key = conf.load_validator_key();

// let b = serde_json::to_vec(&priv_validator_key.priv_key).unwrap();
let mut b = priv_validator_key.priv_key.ed25519_signing_key().unwrap().as_bytes().to_vec();

b.extend(priv_validator_key.pub_key.to_bytes());
let local_key = SecretKey::new(b.as_slice().try_into().unwrap());

let id = frost::Secp256K1ScalarField::deserialize(&local_key.public_key().as_slice().try_into().unwrap()).unwrap();
let identifier = frost_core::Identifier::new(id).unwrap();
Expand All @@ -88,7 +88,7 @@ impl Signer {
let bitcoin_client = Client::new(
&conf.bitcoin.rpc,
Auth::UserPass(conf.bitcoin.user.clone(), conf.bitcoin.password.clone()))
.expect("Could not initial bitcoin RPC client");
.expect("Could not initial bitcoin RPC client");

let db_sign = sled::open(conf.get_database_with_name("sign-task")).expect("Counld not create database!");
let db_sign_variables = sled::open(conf.get_database_with_name("sign-task-variables")).expect("Counld not create database!");
Expand Down Expand Up @@ -117,11 +117,38 @@ impl Signer {
&self.identifier
}

pub fn peer_id(&self) -> PeerId {
identifier_to_peer_id(&self.identifier)
}

pub fn p2p_keypair(&self) -> Keypair {
let raw = &self.identity_key.to_vec()[0..32].to_vec();
Keypair::ed25519_from_bytes(raw.clone()).unwrap()
}

pub fn validator_address(&self) -> String {
match &self.config().get_validator_key() {
Some(key) => key.address.clone(),
None => "".to_string()
self.config().load_validator_key().address.to_string()
}

pub fn is_white_listed_peer(&self, peer_id: PeerId) -> bool {

if self.config.bootstrap_nodes.iter().any(|addr| {addr.contains(&peer_id.to_base58())}) {
return true
}
let keypairs = self.list_keypairs();
if keypairs.len() == 0 {
return true;
}
for (_, k) in keypairs {
for identifier in k.pub_key.verifying_shares().keys() {
let local = identifier_to_peer_id(identifier);
// println!("{:?}={:?} {}", local, peer_id, local==peer_id);
if local == peer_id {
return true;
}
}
}
false
}

pub async fn get_relayer_account(&self) -> BaseAccount {
Expand Down Expand Up @@ -376,15 +403,18 @@ pub async fn run_signer_daemon(conf: Config, seed: bool) {
info!("Starting TSS Signer Daemon");

// load config
conf.load_validator_key();
let signer = Signer::new(conf.clone());

for (i, (addr, vkp) ) in signer.list_keypairs().iter().enumerate() {
debug!("Vault {i}. {addr}, ({}-of-{})", vkp.priv_key.min_signers(), vkp.pub_key.verifying_shares().len());
}

let libp2p_keypair = Keypair::from_protobuf_encoding(from_base64(&conf.p2p_keypair).unwrap().as_slice()).unwrap();
let mut swarm: libp2p::Swarm<TSSBehaviour> = libp2p::SwarmBuilder::with_existing_identity(libp2p_keypair)
// let priv_validator_key = conf.load_validator_key();
// let bytes = serde_json::to_vec(&priv_validator_key.priv_key).unwrap();
// let libp2p_keypair = Keypair::from_protobuf_encoding(bytes.as_slice()).unwrap();
// let mut raw = signer.identity_key.as_slice().to_owned();
// let libp2p_keypair = Keypair::ed25519_from_bytes(&mut raw).unwrap();
let mut swarm: libp2p::Swarm<TSSBehaviour> = libp2p::SwarmBuilder::with_existing_identity(signer.p2p_keypair())
.with_tokio()
.with_tcp(
tcp::Config::default(),
Expand Down Expand Up @@ -446,9 +476,6 @@ pub async fn run_signer_daemon(conf: Config, seed: bool) {
subscribe_gossip_topics(&mut swarm);

let mut interval_free = tokio::time::interval(TASK_INTERVAL);
// let start = Instant::now() + (TASK_ROUND_WINDOW - tokio::time::Duration::from_secs(now() % TASK_ROUND_WINDOW.as_secs()));
// let mut interval_aligned = tokio::time::interval_at(start, TASK_ROUND_WINDOW);
// let mut alive_interval = tokio::time::interval(tokio::time::Duration::from_secs(5));

loop {
select! {
Expand All @@ -460,9 +487,14 @@ pub async fn run_signer_daemon(conf: Config, seed: bool) {
info!("Listening on {address}/p2p/{}", swarm.local_peer_id());
},
SwarmEvent::ConnectionEstablished { peer_id, endpoint, ..} => {
swarm.behaviour_mut().gossip.add_explicit_peer(&peer_id);
let addr = endpoint.get_remote_address();
info!("Connected to {:?}, ", addr);
if signer.is_white_listed_peer(peer_id) {
swarm.behaviour_mut().gossip.add_explicit_peer(&peer_id);
let addr = endpoint.get_remote_address();
info!("Connected to {:?}, ", addr);
} else {
let _ = swarm.disconnect_peer_id(peer_id);
info!("Disconnected (untrusted) {:?}", peer_id);
}
},
SwarmEvent::ConnectionClosed { peer_id, cause, .. } => {
info!("Disconnected {peer_id}: {:?}", cause);
Expand Down Expand Up @@ -540,11 +572,17 @@ async fn event_handler(event: TSSBehaviourEvent, swarm: &mut Swarm<TSSBehaviour>
// info!(" @@(Received) Discovered new peer: {peer_id} with info: {connection_id} {:?}", info);
info.listen_addrs.iter().for_each(|addr| {
if !addr.to_string().starts_with("/ip4/127.0.0.1") {
// tracing::debug!("Discovered: {addr}/p2p/{peer_id}");
tracing::debug!("Discovered: {addr}/p2p/{peer_id}");
swarm.behaviour_mut().kad.add_address(&peer_id, addr.clone());
}
});
}
TSSBehaviourEvent::Kad(kad::Event::RoutablePeer { peer, address }) => {
debug!("Found Peer {:?}/{:?}", address, peer)
}
TSSBehaviourEvent::Kad(kad::Event::RoutingUpdated { is_new_peer, addresses, .. }) => {
debug!("Routing Peer {:?}/{:?}", addresses, is_new_peer)
}
TSSBehaviourEvent::Mdns(mdns::Event::Discovered(list)) => {
for (peer_id, multiaddr) in list {
swarm.behaviour_mut().gossip.add_explicit_peer(&peer_id);
Expand Down
7 changes: 5 additions & 2 deletions src/commands/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ use super::Cli;

pub fn execute(cli: &Cli) {
let conf = Config::from_file(&cli.home).unwrap();
let signer = Signer::new(conf.clone());

println!("\n{:?}", signer.identifier());
println!("{:?}\n", signer.peer_id());

println!("Relayer address");
println!("-------------------------------------------------------------");
println!(" {}", conf.relayer_bitcoin_address());
println!("\n NOTE: Please fund relayer address on sidechain before using it.");
println!("-------------------------------------------------------------");

let conf = Config::from_file(&cli.home).unwrap();
let signer = Signer::new(conf);
println!("\nVault Address:");
signer.list_keypairs().iter().enumerate().for_each(| (i, (addr, kp))| {
println!("{i}. {addr} ({}-of-{})", kp.priv_key.min_signers(), kp.pub_key.verifying_shares().len());
Expand Down
18 changes: 0 additions & 18 deletions src/commands/id.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ pub enum Commands {
Debug {
txid: String,
},
Id,
Test {
#[clap(long, default_value = "shuttler")]
bin: String,
Expand All @@ -66,5 +65,4 @@ pub mod reset;
pub mod submit_header;
pub mod submit_tx;
pub mod debug;
pub mod id;
pub mod test;
6 changes: 6 additions & 0 deletions src/helper/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine};
use bitcoin_hashes::sha256;
use bitcoin_hashes::Hash;
use frost_secp256k1_tr::Identifier;
use libp2p::PeerId;

pub fn to_base64(input: &[u8]) -> String {
// base64::encode(data)
Expand All @@ -21,3 +22,8 @@ pub fn hash(bytes: &[u8]) -> String {
pub fn abbr(identifier: &Identifier) -> String {
hex::encode(identifier.serialize())[..4].to_lowercase()
}

pub fn identifier_to_peer_id(identifier: &Identifier) -> PeerId {
let xkey = libp2p::identity::ed25519::PublicKey::try_from_bytes(&identifier.serialize()).unwrap();
libp2p::identity::PublicKey::from(xkey).to_peer_id()
}
Loading

0 comments on commit 716ea88

Please sign in to comment.