Skip to content

Commit

Permalink
refurbish bls signature verification
Browse files Browse the repository at this point in the history
  • Loading branch information
seunlanlege committed Dec 9, 2024
1 parent 7c8cf40 commit 4287c5e
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 147 deletions.
8 changes: 1 addition & 7 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ hex = { version = "0.4.3", default-features = false }
hex-literal = "0.4.1"
rlp = { version = "0.5.1", default-features = false }

# arkworks
ark-ec = { version = "0.4.2", default-features = false }
bls = { package = "bls_on_arkworks", version = "0.2.2", default-features = false }


orml-xcm-support = { version = "=1.0.0", default-features = false }
orml-traits = { version = "=1.0.0", default-features = false }
primitive-types = { version = "0.12.2", default-features = false }
Expand Down
1 change: 0 additions & 1 deletion evm/src/modules/GnosisUniswapV2Interface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ contract GnosisUniswapV2Interface {
return out;
}


/**
* @dev Returns the quoted amount for the dispatch.
*/
Expand Down
8 changes: 3 additions & 5 deletions modules/consensus/bsc/verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ ismp = { workspace = true, default-features = false }
geth-primitives = { workspace = true, default-features = false }
sync-committee-verifier = { workspace = true, default-features = false }
sync-committee-primitives = { workspace = true, default-features = false }
ark-bls12-381 = { version = "0.4.0", default-features = false, features = ["curve"] }
bls = { package = "bls_on_arkworks", version = "0.2.2", default-features = false }
ark-ec = { version = "0.4.2", default-features = false }
ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false }
bls = { workspace = true }
ark-ec = { workspace = true }
sp-runtime = { workspace = true }
ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false }

[features]
default = ["std"]
Expand All @@ -36,7 +35,6 @@ std = [
"ismp/std",
"alloy-primitives/std",
"alloy-rlp/std",
"ark-bls12-381/std",
"bls/std",
"sync-committee-verifier/std",
"sync-committee-primitives/std",
Expand Down
65 changes: 16 additions & 49 deletions modules/consensus/bsc/verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@
#[warn(unused_variables)]
use alloc::vec::Vec;
use anyhow::anyhow;
use ark_ec::AffineRepr;
use bls::{point_to_pubkey, types::G1ProjectivePoint};
use geth_primitives::{CodecHeader, Header};
use ismp::messaging::Keccak256;
use primitives::{parse_extra, BscClientUpdate, Config, EPOCH_LENGTH, VALIDATOR_BIT_SET_SIZE};
use sp_core::H256;
use sync_committee_verifier::crypto::{pairing, pubkey_to_projective};
pub mod primitives;
use bls::{
point_to_pubkey, pubkey_to_point,
types::{G1AffinePoint, G1ProjectivePoint, Signature},
DST_ETHEREUM,
};
use geth_primitives::{CodecHeader, Header};
use ssz_rs::{Bitvector, Deserialize};
use sync_committee_primitives::constants::BlsPublicKey;
use sync_committee_verifier::crypto::pubkey_to_projective;

pub mod primitives;

extern crate alloc;

Expand Down Expand Up @@ -69,17 +65,20 @@ pub fn verify_bsc_header<H: Keccak256, C: Config>(
.filter_map(|(validator, bit)| if *bit { Some(validator.clone()) } else { None })
.collect();

let aggregate_public_key = aggregate_public_keys(&participants)
.as_slice()
.try_into()
.map_err(|_| anyhow!("Could not aggregate public keys"))?;

let aggregate_public_key = aggregate_public_keys(&participants);
let msg = H::keccak256(alloy_rlp::encode(extra_data.vote_data.clone()).as_slice());

let signature = extra_data.agg_signature;

verify_aggregate_signature(&aggregate_public_key, msg.0.to_vec(), signature.to_vec().as_ref())
.map_err(|_| anyhow!("Could not verify aggregate signature"))?;
let verify = bls::verify(
&aggregate_public_key,
&msg.as_ref().to_vec(),
signature.to_vec().as_ref(),
&bls::DST_ETHEREUM.as_bytes().to_vec(),
);

if !verify {
Err(anyhow!("Could not verify aggregate signature"))?
}

let next_validator_addresses: Option<NextValidators> =
// If an epoch ancestry was provided, we try to extract the next validator set from it
Expand Down Expand Up @@ -154,35 +153,3 @@ pub fn aggregate_public_keys(keys: &[BlsPublicKey]) -> Vec<u8> {

point_to_pubkey(aggregate.into())
}

// we need a bls-utils crate
pub fn verify_aggregate_signature(
aggregate: &BlsPublicKey,
msg: Vec<u8>,
signature: &Signature,
) -> anyhow::Result<()> {
let aggregate_key_point: G1AffinePoint =
pubkey_to_point(aggregate).map_err(|_| anyhow!("Could not convert public key to point"))?;
let signature = bls::signature_to_point(signature).map_err(|e| anyhow!("{:?}", e))?;

if !bls::signature_subgroup_check(signature) {
Err(anyhow!("Signature not in subgroup"))?
}

let q = bls::hash_to_point(&msg, &DST_ETHEREUM.as_bytes().to_vec());
let c1 = pairing(q, aggregate_key_point);

// From the spec:
// > When the signature variant is minimal-pubkey-size, P is the distinguished point P1 that
// > generates the group G1.
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#section-2.2>
let p = G1AffinePoint::generator();

let c2 = pairing(signature, p);

if c1 == c2 {
Ok(())
} else {
Err(anyhow!("Aggregate signature verification failed"))
}
}
6 changes: 0 additions & 6 deletions modules/consensus/sync-committee/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ serde-hex-utils = { workspace = true, default-features = false }

ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false }

ark-ec = { version = "0.4.2", default-features = false }
ark-bls12-381 = { version = "0.4.0", default-features = false }
bls_on_arkworks = { version = "0.2.2", default-features = false }

[features]
default = ["std"]
Expand All @@ -28,9 +25,6 @@ std = [
'codec/std',
"primitive-types/std",
"anyhow/std",
"ark-ec/std",
"bls_on_arkworks/std",
"ark-bls12-381/std",
"primitive-types/std",
"serde",
"serde-hex-utils/std"
Expand Down
15 changes: 1 addition & 14 deletions modules/consensus/sync-committee/prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ reqwest-middleware = "0.2.4"
reqwest-chain = "0.1.0"
base2 = { version= "0.3.1" }
env_logger = "0.10.0"
ark-ec = { version = "0.4.2" }
ark-bls12-381 = { version = "0.4.0" }
bls_on_arkworks = { version = "0.2.2" }
bls = { workspace = true, default-features = true }
async-trait = "0.1.77"
tracing = "0.1.40"

Expand All @@ -37,14 +35,3 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"]}
parity-scale-codec = "3.2.2"
reqwest-eventsource = "0.4.0"
dotenv = "0.15.0"

[features]
default = ["std"]
std = [
"ssz-rs/default",
"sync-committee-primitives/std",
"anyhow/std",
"ark-ec/std",
"bls_on_arkworks/std",
"ark-bls12-381/std"
]
2 changes: 1 addition & 1 deletion modules/consensus/sync-committee/prover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
routes::*,
};
use anyhow::anyhow;
use bls_on_arkworks::{point_to_pubkey, types::G1ProjectivePoint};
use bls::{point_to_pubkey, types::G1ProjectivePoint};
use log::trace;
use primitive_types::H256;
use reqwest::{Client, Url};
Expand Down
9 changes: 4 additions & 5 deletions modules/consensus/sync-committee/verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ sync-committee-primitives = { workspace = true, default-features = false }
log = { workspace = true, default-features = false }
anyhow = { workspace = true, default-features = false }
ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false }
ark-ec = { version = "0.4.2", default-features = false }
ark-bls12-381 = { version = "0.4.0", default-features = false, features = ["curve"] }
bls = { package = "bls_on_arkworks", version = "0.2.2", default-features = false }
bls = { workspace = true }
ark-ec = { workspace = true }

[features]
default = ["std"]
Expand All @@ -22,10 +21,10 @@ std = [
"sync-committee-primitives/std",
"log/std",
"anyhow/std",
"bls/std",
"ark-ec/std",
"ark-bls12-381/std",
"bls/std"
]

[dev-dependencies]
hex = "0.4.3"
hex-literal = { workspace = true, default-features = true }
74 changes: 19 additions & 55 deletions modules/consensus/sync-committee/verifier/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use alloc::vec::Vec;
use anyhow::anyhow;
use ark_bls12_381::Bls12_381;
use ark_ec::{pairing::Pairing, AffineRepr};
use bls::{
types::{BLS12381Pairing, G1AffinePoint, G1ProjectivePoint, G2AffinePoint, Signature},
DST_ETHEREUM,
};
use bls::types::G1ProjectivePoint;
use sync_committee_primitives::constants::BlsPublicKey;

pub fn pubkey_to_projective(compressed_key: &BlsPublicKey) -> anyhow::Result<G1ProjectivePoint> {
Expand All @@ -14,7 +9,7 @@ pub fn pubkey_to_projective(compressed_key: &BlsPublicKey) -> anyhow::Result<G1P
Ok(affine_point.into())
}

fn subtract_points_from_aggregate(
pub fn subtract_points_from_aggregate(
aggregate: &BlsPublicKey,
points: &[BlsPublicKey],
) -> anyhow::Result<G1ProjectivePoint> {
Expand All @@ -27,48 +22,13 @@ fn subtract_points_from_aggregate(
Ok(subset_aggregate)
}

pub fn pairing(u: G2AffinePoint, v: G1AffinePoint) -> BLS12381Pairing {
Bls12_381::pairing(v, u)
}

/// Adapted from https://github.com/ArnaudBrousseau/bls_on_arkworks/blob/main/src/lib.rs#L335
/// Verifies an aggregate bls12-381 signature from ethereum sync-committee
/// Expects signature subgroup to be valid
pub fn verify_aggregate_signature(
aggregate: &BlsPublicKey,
non_participants: &[BlsPublicKey],
msg: Vec<u8>,
signature: &Signature,
) -> anyhow::Result<()> {
let subset_aggregate = subtract_points_from_aggregate(aggregate, non_participants)?;
let aggregate_key_point: G1AffinePoint = subset_aggregate.into();
let signature = bls::signature_to_point(signature).map_err(|e| anyhow!("{:?}", e))?;

if !bls::signature_subgroup_check(signature) {
Err(anyhow!("Signature not in subgroup"))?
}

let q = bls::hash_to_point(&msg, &DST_ETHEREUM.as_bytes().to_vec());
let c1 = pairing(q, aggregate_key_point);

// From the spec:
// > When the signature variant is minimal-pubkey-size, P is the distinguished point P1 that
// > generates the group G1.
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#section-2.2>
let p = G1AffinePoint::generator();

let c2 = pairing(signature, p);

if c1 == c2 {
Ok(())
} else {
Err(anyhow!("Aggregate signature verification failed"))
}
}

#[cfg(test)]
mod tests {
use crate::crypto::verify_aggregate_signature;
use ark_ec::CurveGroup;
use sync_committee_primitives::constants::BlsPublicKey;

use crate::crypto::subtract_points_from_aggregate;
use hex_literal::hex;

#[test]
fn test_signature_verification() {
Expand All @@ -89,22 +49,26 @@ mod tests {
hex::decode("813a89a296973e35545cfa74fe3efd172a7d19443c97c625d699e9737229b0a2")
.unwrap();
let aggregate_signature = hex::decode("a1abfcf9bd54b7a003e1f45f7543b194d8d25b816577b02ee4f1c99aa9821c620be6ecedbc8c5fab64d343a6cc832040029040e591fa24db54f5441f28d73918775e8feeac6177c9e016d2576b982d1cce453896a8aace2bda7374e5a76ce213").unwrap();
let aggregate_pub_key = hex::decode("a3f2da752bd1dfc7288b46cc061668856e0cefa93ba6e8ff4699f355138f63a541fdb3444ddebcdce695d6313fa4b244").unwrap().try_into().unwrap();
let aggregate_pub_key: BlsPublicKey = hex!("a3f2da752bd1dfc7288b46cc061668856e0cefa93ba6e8ff4699f355138f63a541fdb3444ddebcdce695d6313fa4b244").to_vec().try_into().unwrap();

let bit_vector = hex::decode("01000100010001000100").unwrap();

let non_participants = pks
.into_iter()
.zip(bit_vector)
.filter_map(|(pk, bit)| if bit == 0 { Some(pk.try_into().unwrap()) } else { None })
.collect::<Vec<_>>();
.collect::<Vec<BlsPublicKey>>();

let aggregate =
subtract_points_from_aggregate(&aggregate_pub_key, &non_participants).unwrap();

verify_aggregate_signature(
&aggregate_pub_key,
&non_participants,
message,
let verify = bls::verify(
&bls::point_to_pubkey(aggregate.into_affine()),
&message,
&aggregate_signature,
)
.unwrap()
&bls::DST_ETHEREUM.as_bytes().to_vec(),
);

assert!(verify, "Signature verification failed");
}
}
2 changes: 2 additions & 0 deletions modules/consensus/sync-committee/verifier/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub enum Error {
InvalidMerkleBranch(String),
InvalidRoot(String),
MerkleizationError(String),
BlsError(bls::errors::BLSError),
SignatureVerification,
}

Expand All @@ -20,6 +21,7 @@ impl Display for Error {
},
Error::InvalidUpdate(err) => write!(f, "Invalid update {err:?}"),
Error::DomainError => write!(f, "Couldn't get domain"),
Error::BlsError(err) => write!(f, "BlsError: {err:?}"),
Error::InvalidMerkleBranch(err) => write!(f, "Invalid merkle branch {err:?}"),
Error::InvalidRoot(err) => write!(f, "Invalid root {err:?}"),
Error::MerkleizationError(err) => write!(f, "Merkleization error {err:?}"),
Expand Down
Loading

0 comments on commit 4287c5e

Please sign in to comment.