Skip to content

Commit 716ea88

Browse files
author
hashkid
authored
Merge pull request #35 from sideprotocol/internal/latency-test
Add a whitelist of peers to safeguard against spam attacks.
2 parents 774bddf + 5f9427e commit 716ea88

File tree

12 files changed

+130
-89
lines changed

12 files changed

+130
-89
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "shuttler"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

src/app/config.rs

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ use cosmos_sdk_proto::cosmos::auth::v1beta1::{query_client::QueryClient as AuthQ
44

55
use frost_secp256k1_tr::keys::{KeyPackage, PublicKeyPackage};
66
use serde::{Deserialize, Serialize};
7-
use tracing::error;
7+
use tendermint_config::PrivValidatorKey;
88
use std::{fs, path::PathBuf, str::FromStr, sync::Mutex, time::Duration};
9-
10-
use crate::helper::{cipher::random_bytes, encoding::to_base64};
9+
use crate::helper::cipher::random_bytes;
1110

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

@@ -26,7 +25,7 @@ lazy_static! {
2625
pub struct Config {
2726
#[serde(skip_serializing, skip_deserializing)]
2827
pub home: PathBuf,
29-
pub p2p_keypair: String,
28+
// pub p2p_keypair: String,
3029
pub port: u32,
3130
pub bootstrap_nodes: Vec<String>,
3231
/// logger level
@@ -104,13 +103,6 @@ pub struct AnyKey {
104103
pub value: String,
105104
}
106105

107-
#[derive(Debug, Serialize, Deserialize, Clone)]
108-
pub struct PrivValidatorKey {
109-
pub address: String,
110-
pub pub_key: AnyKey,
111-
pub priv_key: AnyKey,
112-
}
113-
114106
/// relayer account will be used to sign transactions on the side chain,
115107
/// such as sending block headers, depositing and withdrawing transactions
116108
pub async fn get_relayer_account(conf: &Config) -> BaseAccount {
@@ -154,24 +146,23 @@ pub fn remove_relayer_account() {
154146
}
155147

156148
impl Config {
157-
pub fn load_validator_key(&self) {
149+
pub fn load_validator_key(&self) -> PrivValidatorKey {
158150
let priv_key_path = if self.priv_validator_key_path.starts_with("/") {
159151
PathBuf::from(self.priv_validator_key_path.clone())
160152
} else {
161153
self.home.join(self.priv_validator_key_path.clone())
162154
};
163-
match fs::read_to_string(priv_key_path.clone()) {
164-
Ok(text) => {
165-
let prv_key = serde_json::from_str::<PrivValidatorKey>(text.as_str()).expect("Failed to parse priv_validator_key.json");
166-
PRIV_VALIDATOR_KEY.lock().unwrap().replace(prv_key.clone());
167-
},
168-
Err(e) => error!("You are running in Bootstrap mode because the 'priv_validator_key.json' has not been loaded: {}", e)
169-
};
155+
let text = fs::read_to_string(priv_key_path.clone()).expect("priv_validator_key.json does not exists!");
156+
157+
let prv_key = serde_json::from_str::<PrivValidatorKey>(text.as_str()).expect("Failed to parse priv_validator_key.json");
158+
// PRIV_VALIDATOR_KEY.lock().unwrap().replace(prv_key.clone());
159+
prv_key
160+
170161
}
171162

172-
pub fn get_validator_key(&self) -> Option<PrivValidatorKey> {
173-
PRIV_VALIDATOR_KEY.lock().unwrap().clone()
174-
}
163+
// pub fn get_validator_key(&self) -> Option<PrivValidatorKey> {
164+
// PRIV_VALIDATOR_KEY.lock().unwrap().clone()
165+
// }
175166

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

@@ -193,25 +184,40 @@ impl Config {
193184
Ok(config)
194185
}
195186

187+
pub fn generate_priv_validator_key(home: PathBuf) {
188+
let rng = rand::thread_rng();
189+
let sk = ed25519_consensus::SigningKey::new(rng);
190+
let priv_key = tendermint::private_key::PrivateKey::from_ed25519_consensus(sk);
191+
192+
let key = tendermint_config::PrivValidatorKey {
193+
address: tendermint::account::Id::from(priv_key.public_key()),
194+
pub_key: priv_key.public_key(),
195+
priv_key,
196+
};
197+
198+
fs::create_dir_all(&home).unwrap();
199+
let text= serde_json::to_string_pretty(&key).unwrap();
200+
fs::write(home.join("priv_validator_key.json"), text).unwrap();
201+
}
202+
196203
pub fn default(home_str: &str, port: u32, network: Network) -> Self {
197204
let entropy = random_bytes(32);
198205
let mnemonic = bip39::Mnemonic::from_entropy(entropy.as_slice()).expect("failed to create mnemonic");
199-
let p2p_keypair = to_base64(libp2p::identity::Keypair::generate_ed25519().to_protobuf_encoding().unwrap().as_slice());
206+
// let p2p_keypair = to_base64(libp2p::identity::Keypair::generate_ed25519().to_protobuf_encoding().unwrap().as_slice());
200207
let home = if home_str.starts_with("/") {
201208
PathBuf::from_str(home_str).unwrap()
202209
} else {
203210
home_dir(home_str)
204211
};
212+
Self::generate_priv_validator_key(home.clone());
205213
Self {
206214
home,
207-
p2p_keypair ,
215+
// p2p_keypair ,
208216
port: port as u32,
209-
bootstrap_nodes: vec!["/ip4/192.248.180.245/tcp/5158/p2p/12D3KooWMpMtmYQKSn1sZaSRn4CAcsraWZVrZ2zdNjEgsEPSd3Pv".to_string()],
217+
bootstrap_nodes: vec![],
210218
log_level: "debug".to_string(),
211219
mnemonic: mnemonic.to_string(),
212220
priv_validator_key_path: "priv_validator_key.json".to_string(),
213-
// keys: BTreeMap::new(),
214-
// pubkeys: BTreeMap::new(),
215221
bitcoin: BitcoinCfg {
216222
network,
217223
rpc: "http://192.248.150.102:18332".to_string(),

src/app/signer.rs

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ use libp2p::identity::Keypair;
1515
use libp2p::kad::store::MemoryStore;
1616

1717
use libp2p::swarm::SwarmEvent;
18-
use libp2p::{ gossipsub, identify, mdns, noise, tcp, yamux, Multiaddr, PeerId, Swarm};
18+
use libp2p::{ gossipsub, identify, kad, mdns, noise, tcp, yamux, Multiaddr, PeerId, Swarm};
1919
use serde::Serialize;
2020

2121
use crate::app::config::{self, TASK_INTERVAL};
2222
use crate::app::config::Config;
2323
use crate::helper::bitcoin::get_group_address_by_tweak;
24-
use crate::helper::cipher::random_bytes;
25-
use crate::helper::encoding::from_base64;
24+
use crate::helper::encoding::identifier_to_peer_id;
2625
use crate::helper::gossip::{subscribe_gossip_topics, HeartBeatMessage, SubscribeTopic};
2726
use crate::helper::mem_store;
2827
use crate::protocols::sign::{received_sign_message, SignMesage, SignTask};
@@ -66,19 +65,20 @@ pub struct Signer {
6665
db_dkg: sled::Db,
6766
db_dkg_variables: sled::Db,
6867
db_keypair: sled::Db,
68+
6969
}
7070

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

7474
// load private key from priv_validator_key_path
75-
let local_key = match conf.get_validator_key() {
76-
Some(validator_key) => {
77-
let b = from_base64(&validator_key.priv_key.value).expect("Decode private key failed");
78-
SecretKey::from_slice(b.as_slice()).expect("invalid secret key")
79-
},
80-
None => SecretKey::from_slice(random_bytes(SecretKey::BYTES).as_slice()).expect("invalid secret key")
81-
};
75+
let priv_validator_key = conf.load_validator_key();
76+
77+
// let b = serde_json::to_vec(&priv_validator_key.priv_key).unwrap();
78+
let mut b = priv_validator_key.priv_key.ed25519_signing_key().unwrap().as_bytes().to_vec();
79+
80+
b.extend(priv_validator_key.pub_key.to_bytes());
81+
let local_key = SecretKey::new(b.as_slice().try_into().unwrap());
8282

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

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

120+
pub fn peer_id(&self) -> PeerId {
121+
identifier_to_peer_id(&self.identifier)
122+
}
123+
124+
pub fn p2p_keypair(&self) -> Keypair {
125+
let raw = &self.identity_key.to_vec()[0..32].to_vec();
126+
Keypair::ed25519_from_bytes(raw.clone()).unwrap()
127+
}
128+
120129
pub fn validator_address(&self) -> String {
121-
match &self.config().get_validator_key() {
122-
Some(key) => key.address.clone(),
123-
None => "".to_string()
130+
self.config().load_validator_key().address.to_string()
131+
}
132+
133+
pub fn is_white_listed_peer(&self, peer_id: PeerId) -> bool {
134+
135+
if self.config.bootstrap_nodes.iter().any(|addr| {addr.contains(&peer_id.to_base58())}) {
136+
return true
124137
}
138+
let keypairs = self.list_keypairs();
139+
if keypairs.len() == 0 {
140+
return true;
141+
}
142+
for (_, k) in keypairs {
143+
for identifier in k.pub_key.verifying_shares().keys() {
144+
let local = identifier_to_peer_id(identifier);
145+
// println!("{:?}={:?} {}", local, peer_id, local==peer_id);
146+
if local == peer_id {
147+
return true;
148+
}
149+
}
150+
}
151+
false
125152
}
126153

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

378405
// load config
379-
conf.load_validator_key();
380406
let signer = Signer::new(conf.clone());
381407

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

386-
let libp2p_keypair = Keypair::from_protobuf_encoding(from_base64(&conf.p2p_keypair).unwrap().as_slice()).unwrap();
387-
let mut swarm: libp2p::Swarm<TSSBehaviour> = libp2p::SwarmBuilder::with_existing_identity(libp2p_keypair)
412+
// let priv_validator_key = conf.load_validator_key();
413+
// let bytes = serde_json::to_vec(&priv_validator_key.priv_key).unwrap();
414+
// let libp2p_keypair = Keypair::from_protobuf_encoding(bytes.as_slice()).unwrap();
415+
// let mut raw = signer.identity_key.as_slice().to_owned();
416+
// let libp2p_keypair = Keypair::ed25519_from_bytes(&mut raw).unwrap();
417+
let mut swarm: libp2p::Swarm<TSSBehaviour> = libp2p::SwarmBuilder::with_existing_identity(signer.p2p_keypair())
388418
.with_tokio()
389419
.with_tcp(
390420
tcp::Config::default(),
@@ -446,9 +476,6 @@ pub async fn run_signer_daemon(conf: Config, seed: bool) {
446476
subscribe_gossip_topics(&mut swarm);
447477

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

453480
loop {
454481
select! {
@@ -460,9 +487,14 @@ pub async fn run_signer_daemon(conf: Config, seed: bool) {
460487
info!("Listening on {address}/p2p/{}", swarm.local_peer_id());
461488
},
462489
SwarmEvent::ConnectionEstablished { peer_id, endpoint, ..} => {
463-
swarm.behaviour_mut().gossip.add_explicit_peer(&peer_id);
464-
let addr = endpoint.get_remote_address();
465-
info!("Connected to {:?}, ", addr);
490+
if signer.is_white_listed_peer(peer_id) {
491+
swarm.behaviour_mut().gossip.add_explicit_peer(&peer_id);
492+
let addr = endpoint.get_remote_address();
493+
info!("Connected to {:?}, ", addr);
494+
} else {
495+
let _ = swarm.disconnect_peer_id(peer_id);
496+
info!("Disconnected (untrusted) {:?}", peer_id);
497+
}
466498
},
467499
SwarmEvent::ConnectionClosed { peer_id, cause, .. } => {
468500
info!("Disconnected {peer_id}: {:?}", cause);
@@ -540,11 +572,17 @@ async fn event_handler(event: TSSBehaviourEvent, swarm: &mut Swarm<TSSBehaviour>
540572
// info!(" @@(Received) Discovered new peer: {peer_id} with info: {connection_id} {:?}", info);
541573
info.listen_addrs.iter().for_each(|addr| {
542574
if !addr.to_string().starts_with("/ip4/127.0.0.1") {
543-
// tracing::debug!("Discovered: {addr}/p2p/{peer_id}");
575+
tracing::debug!("Discovered: {addr}/p2p/{peer_id}");
544576
swarm.behaviour_mut().kad.add_address(&peer_id, addr.clone());
545577
}
546578
});
547579
}
580+
TSSBehaviourEvent::Kad(kad::Event::RoutablePeer { peer, address }) => {
581+
debug!("Found Peer {:?}/{:?}", address, peer)
582+
}
583+
TSSBehaviourEvent::Kad(kad::Event::RoutingUpdated { is_new_peer, addresses, .. }) => {
584+
debug!("Routing Peer {:?}/{:?}", addresses, is_new_peer)
585+
}
548586
TSSBehaviourEvent::Mdns(mdns::Event::Discovered(list)) => {
549587
for (peer_id, multiaddr) in list {
550588
swarm.behaviour_mut().gossip.add_explicit_peer(&peer_id);

src/commands/address.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ use super::Cli;
44

55
pub fn execute(cli: &Cli) {
66
let conf = Config::from_file(&cli.home).unwrap();
7+
let signer = Signer::new(conf.clone());
8+
9+
println!("\n{:?}", signer.identifier());
10+
println!("{:?}\n", signer.peer_id());
11+
712
println!("Relayer address");
813
println!("-------------------------------------------------------------");
914
println!(" {}", conf.relayer_bitcoin_address());
1015
println!("\n NOTE: Please fund relayer address on sidechain before using it.");
1116
println!("-------------------------------------------------------------");
1217

13-
let conf = Config::from_file(&cli.home).unwrap();
14-
let signer = Signer::new(conf);
1518
println!("\nVault Address:");
1619
signer.list_keypairs().iter().enumerate().for_each(| (i, (addr, kp))| {
1720
println!("{i}. {addr} ({}-of-{})", kp.priv_key.min_signers(), kp.pub_key.verifying_shares().len());

src/commands/id.rs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/commands/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ pub enum Commands {
4646
Debug {
4747
txid: String,
4848
},
49-
Id,
5049
Test {
5150
#[clap(long, default_value = "shuttler")]
5251
bin: String,
@@ -66,5 +65,4 @@ pub mod reset;
6665
pub mod submit_header;
6766
pub mod submit_tx;
6867
pub mod debug;
69-
pub mod id;
7068
pub mod test;

src/helper/encoding.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine};
22
use bitcoin_hashes::sha256;
33
use bitcoin_hashes::Hash;
44
use frost_secp256k1_tr::Identifier;
5+
use libp2p::PeerId;
56

67
pub fn to_base64(input: &[u8]) -> String {
78
// base64::encode(data)
@@ -21,3 +22,8 @@ pub fn hash(bytes: &[u8]) -> String {
2122
pub fn abbr(identifier: &Identifier) -> String {
2223
hex::encode(identifier.serialize())[..4].to_lowercase()
2324
}
25+
26+
pub fn identifier_to_peer_id(identifier: &Identifier) -> PeerId {
27+
let xkey = libp2p::identity::ed25519::PublicKey::try_from_bytes(&identifier.serialize()).unwrap();
28+
libp2p::identity::PublicKey::from(xkey).to_peer_id()
29+
}

0 commit comments

Comments
 (0)