Skip to content

Commit ddc0d74

Browse files
authored
perf: improve attestations efficiency (#1392)
1 parent 5577f9b commit ddc0d74

File tree

2 files changed

+61
-22
lines changed

2 files changed

+61
-22
lines changed

bench/bls.exs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
public_key =
2+
Base.decode16!(
3+
"a491d1b0ecd9bb917989f0e74f0dea0422eac4a873e5e2644f368dffb9a6e20fd6e10c1b77654d067c0618f6e5a7f79a",
4+
case: :mixed
5+
)
6+
7+
message =
8+
Base.decode16!(
9+
"0000000000000000000000000000000000000000000000000000000000000000",
10+
case: :mixed
11+
)
12+
13+
signature =
14+
Base.decode16!(
15+
"b6ed936746e01f8ecf281f020953fbf1f01debd5657c4a383940b020b26507f6076334f91e2366c96e9ab279fb5158090352ea1c5b0c9274504f4f0e7053af24802e51e4568d164fe986834f41e55c8e850ce1f98458c0cfc9ab380b55285a55",
16+
case: :mixed
17+
)
18+
19+
pk = [public_key]
20+
pk_10 = List.duplicate(public_key, 10)
21+
pk_100 = List.duplicate(public_key, 100)
22+
pk_500 = List.duplicate(public_key, 500)
23+
pk_2048 = List.duplicate(public_key, 2048)
24+
25+
Benchee.run(
26+
%{
27+
"1" => fn -> Bls.fast_aggregate_valid?(pk, message, signature) end,
28+
"10" => fn -> Bls.fast_aggregate_valid?(pk_10, message, signature) end,
29+
"100" => fn -> Bls.fast_aggregate_valid?(pk_100, message, signature) end,
30+
"500" => fn -> Bls.fast_aggregate_valid?(pk_500, message, signature) end,
31+
"2048" => fn -> Bls.fast_aggregate_valid?(pk_2048, message, signature) end
32+
},
33+
warmup: 2,
34+
time: 5
35+
)

native/bls_nif/src/lib.rs

+26-22
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ pub(crate) fn bytes_to_binary<'env>(env: Env<'env>, bytes: &[u8]) -> Binary<'env
1010
binary.into()
1111
}
1212

13+
// Deserialize a PublicKey from a slice of bytes.
14+
// Faster than PublicKey::deserialize() as it doesn't validate the key
15+
// Returns Error on invalid BLST encoding or on Infinity Public Key.
16+
fn fast_public_key_deserialize(pk: &[u8]) -> Result<PublicKey, String> {
17+
if pk == &bls::INFINITY_PUBLIC_KEY[..] {
18+
Err("Infinity public Key".to_owned())
19+
} else {
20+
bls::impls::blst::types::PublicKey::from_bytes(pk)
21+
.map_err(|err| format!("BlstError({:?})", err))
22+
.and_then(|pk| {
23+
PublicKey::deserialize_uncompressed(pk.serialize().as_slice())
24+
// This should never be an error as the pk is obtained from an uncompressed valid key
25+
.map_err(|e| format!("Deserialization error: {:?}", e))
26+
})
27+
}
28+
}
29+
1330
#[rustler::nif]
1431
fn sign<'env>(
1532
env: Env<'env>,
@@ -59,7 +76,7 @@ fn verify<'env>(public_key: Binary, message: Binary, signature: Binary) -> Resul
5976
}
6077
let sig = Signature::deserialize(signature.as_slice()).map_err(|err| format!("{:?}", err))?;
6178
let pubkey =
62-
PublicKey::deserialize(public_key.as_slice()).map_err(|err| format!("{:?}", err))?;
79+
fast_public_key_deserialize(public_key.as_slice()).map_err(|err| format!("{:?}", err))?;
6380

6481
Ok(sig.verify(&pubkey, Hash256::from_slice(message.as_slice())))
6582
}
@@ -74,7 +91,7 @@ fn aggregate_verify<'env>(
7491
.map_err(|err| format!("{:?}", err))?;
7592
let pubkeys_result = public_keys
7693
.iter()
77-
.map(|pkb| PublicKey::deserialize(pkb.as_slice()))
94+
.map(|pkb| fast_public_key_deserialize(pkb.as_slice()))
7895
.collect::<Result<Vec<PublicKey>, _>>();
7996
let pubkeys = pubkeys_result.map_err(|err| format!("{:?}", err))?;
8097

@@ -86,7 +103,7 @@ fn aggregate_verify<'env>(
86103
Ok(aggregate_sig.aggregate_verify(&msgs, &pubkey_refs))
87104
}
88105

89-
#[rustler::nif]
106+
#[rustler::nif(schedule = "DirtyCpu")]
90107
fn fast_aggregate_verify<'env>(
91108
public_keys: Vec<Binary>,
92109
message: Binary,
@@ -99,7 +116,8 @@ fn fast_aggregate_verify<'env>(
99116
.map_err(|err| format!("{:?}", err))?;
100117
let pubkeys_result = public_keys
101118
.iter()
102-
.map(|pkb| PublicKey::deserialize(pkb.as_slice()))
119+
.map(|pkb| fast_public_key_deserialize(pkb.as_slice()))
120+
//.map(|pkb| PublicKey::deserialize(pkb.as_slice()))
103121
.collect::<Result<Vec<PublicKey>, _>>();
104122
let pubkeys = pubkeys_result.map_err(|err| format!("{:?}", err))?;
105123

@@ -120,7 +138,7 @@ fn eth_fast_aggregate_verify<'env>(
120138
.map_err(|err| format!("{:?}", err))?;
121139
let pubkeys_result = public_keys
122140
.iter()
123-
.map(|pkb| PublicKey::deserialize(pkb.as_slice()))
141+
.map(|pkb| fast_public_key_deserialize(pkb.as_slice()))
124142
.collect::<Result<Vec<PublicKey>, _>>();
125143
let pubkeys = pubkeys_result.map_err(|err| format!("{:?}", err))?;
126144

@@ -139,7 +157,7 @@ fn eth_aggregate_pubkeys<'env>(
139157
_ => {
140158
let pubkeys_result = public_keys
141159
.iter()
142-
.map(|pkb| PublicKey::deserialize(pkb.as_slice()))
160+
.map(|pkb| fast_public_key_deserialize(pkb.as_slice()))
143161
.collect::<Result<Vec<PublicKey>, _>>();
144162

145163
let pubkeys = pubkeys_result.map_err(|err| format!("{:?}", err))?;
@@ -156,8 +174,7 @@ fn eth_aggregate_pubkeys<'env>(
156174
}
157175
#[rustler::nif]
158176
fn key_validate<'env>(public_key: Binary) -> Result<bool, String> {
159-
let _pubkey =
160-
PublicKey::deserialize(public_key.as_slice()).map_err(|err| format!("{:?}", err))?;
177+
let _pubkey = fast_public_key_deserialize(public_key.as_slice())?;
161178

162179
Ok(true)
163180
}
@@ -173,17 +190,4 @@ fn derive_pubkey<'env>(env: Env<'env>, private_key: Binary) -> Result<Binary<'en
173190
Ok(bytes_to_binary(env, &public_key_bytes))
174191
}
175192

176-
rustler::init!(
177-
"Elixir.Bls",
178-
[
179-
sign,
180-
aggregate,
181-
aggregate_verify,
182-
fast_aggregate_verify,
183-
eth_fast_aggregate_verify,
184-
eth_aggregate_pubkeys,
185-
verify,
186-
key_validate,
187-
derive_pubkey
188-
]
189-
);
193+
rustler::init!("Elixir.Bls");

0 commit comments

Comments
 (0)