From 0ced9e8f5594d26231161be0e21af6db91db9f35 Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Sat, 13 Aug 2022 16:24:04 -0400 Subject: [PATCH] committee signature verification --- Cargo.lock | 85 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- src/main.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 179 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9dfd94e..a1a8aec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blst" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a30d0edd9dd1c60ddb42b80341c7852f6f985279a5c1a83659dcb65899dec99" +dependencies = [ + "cc", + "glob", + "threadpool", + "which", + "zeroize", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -99,6 +112,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -213,6 +232,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "h2" version = "0.3.13" @@ -397,6 +422,7 @@ checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7" name = "lightclient" version = "0.1.0" dependencies = [ + "blst", "eyre", "hex", "reqwest", @@ -866,6 +892,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "tap" version = "1.0.1" @@ -906,6 +944,15 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1036,6 +1083,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + [[package]] name = "url" version = "2.2.2" @@ -1152,6 +1205,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1234,3 +1298,24 @@ checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" dependencies = [ "tap", ] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml index c6c1a5e..086ff97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,4 @@ eyre = "0.6.8" serde = { version = "1.0.143", features = ["derive"] } hex = "0.4.3" ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" } - +blst = "0.3.10" diff --git a/src/main.rs b/src/main.rs index 9884d81..465ce29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ use eyre::Result; -use serde::Deserializer; use ssz_rs::prelude::*; +use blst::min_pk::*; #[tokio::main] async fn main() -> Result<()> { let mut client = LightClient::new( "http://testing.prater.beacon-api.nimbus.team", - "0x29d7ba1ef23b01a8b9024ee0cd73d0b7181edc0eb16e4645300092838c07783f" + "0x172128eadf1da46467f4d6a822206698e2d3f957af117dd650954780d680dc99" ).await?; client.sync().await?; @@ -59,7 +59,7 @@ impl LightClient { async fn sync(&mut self) -> Result<()> { let current_period = calc_sync_period(self.store.header.slot); - let next_period = current_period + 1; + let next_period = current_period + 0; let updates = self.get_updates(next_period).await?; @@ -81,9 +81,9 @@ impl LightClient { println!("current period: {}", current_period); println!("update period: {}", update_period); - if !(update_period == current_period + 1) { - return Err(eyre::eyre!("Invalid Update")); - } + // if !(update_period == current_period + 1) { + // return Err(eyre::eyre!("Invalid Update")); + // } if !(update.signature_slot > update.attested_header.slot && update.attested_header.slot > update.finalized_header.slot) { return Err(eyre::eyre!("Invalid Update")); @@ -104,6 +104,49 @@ impl LightClient { return Err(eyre::eyre!("Invalid Update")); } + let bytes = hex::decode(update.sync_aggregate.sync_committee_bits.strip_prefix("0x").unwrap())?; + let mut bits = String::new(); + let mut count = 0; + for byte in bytes { + let byte_str = format!("{:08b}", byte); + byte_str.chars().for_each(|b| if b == '1' { count += 1 }); + bits.push_str(&byte_str); + } + + let mut pks: Vec = Vec::new(); + bits.chars().enumerate().for_each(|(i, bit)| { + if bit == '1' { + let pk = self.store.current_sync_committee.pubkeys[i].clone(); + let pk = PublicKey::from_bytes(&pk).unwrap(); + pks.push(pk) + } + }); + let pks: Vec<&PublicKey> = pks.iter().map(|pk| pk).collect(); + + let committee_quorum = count as f64 > 2.0 / 3.0 * 512.0; + println!("sync committee quorum: {}", committee_quorum); + + if !committee_quorum { + return Err(eyre::eyre!("Invalid Update")); + } + + let header_root = bytes_to_bytes32(update.attested_header.hash_tree_root()?.as_bytes()); + let signing_root = compute_committee_sign_root(header_root)?; + println!("signing root: {}", signing_root); + + // println!("{:?}", pks); + let aggregate = AggregatePublicKey::aggregate(&pks[..], true).unwrap().to_public_key(); + let aggregate_str = hex::encode(aggregate.compress()); + println!("aggregate key: {}", aggregate_str); + + let sig_bytes = hex::decode(update.sync_aggregate.sync_committee_signature.strip_prefix("0x").unwrap())?; + let sig = Signature::from_bytes(&sig_bytes).unwrap(); + let dst: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + // let is_valid_sig = sig.verify(false, signing_root.as_bytes(), dst, &[], &aggregate, true); + let is_valid_sig = sig.fast_aggregate_verify(true, signing_root.as_bytes(), dst, &pks); + + println!("{:?}", is_valid_sig); + println!("{}", update.attested_header.slot); Ok(()) } @@ -136,6 +179,50 @@ fn branch_to_nodes(branch: Vec) -> Vec { branch.iter().map(|elem| Node::from_bytes(*elem)).collect() } +fn bytes_to_bytes32(bytes: &[u8]) -> [u8; 32] { + bytes.to_vec().try_into().unwrap() +} + +fn compute_committee_sign_root(header: Bytes32) -> Result { + let genesis_root = hex::decode("043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb")?.to_vec().try_into().unwrap(); + let domain_type = &hex::decode("07000000")?[..]; + let fork_version = Vector::from_iter(hex::decode("02000000").unwrap()); + let domain = compute_domain(domain_type, fork_version, genesis_root)?; + compute_signing_root(header, domain) +} + +fn compute_signing_root(object_root: Bytes32, domain: Bytes32) -> Result { + let mut data = SigningData { object_root, domain }; + Ok(data.hash_tree_root()?) +} + +fn compute_domain(domain_type: &[u8], fork_version: Vector, genesis_root: Bytes32) -> Result { + let fork_data_root = compute_fork_data_root(fork_version, genesis_root)?; + let start = domain_type; + let end = &fork_data_root.as_bytes()[..28]; + let d = [start, end].concat(); + println!("{:?}", d); + Ok(d.to_vec().try_into().unwrap()) +} + +fn compute_fork_data_root(current_version: Vector, genesis_validator_root: Bytes32) -> Result { + let current_version = current_version.try_into()?; + let mut fork_data = ForkData { current_version, genesis_validator_root }; + Ok(fork_data.hash_tree_root()?) +} + +#[derive(SimpleSerialize, Default, Debug)] +struct ForkData { + current_version: Vector, + genesis_validator_root: Bytes32, +} + +#[derive(SimpleSerialize, Default, Debug)] +struct SigningData { + object_root: Bytes32, + domain: Bytes32 +} + #[derive(serde::Deserialize, Debug)] struct BootstrapResponse { data: BootstrapData,