diff --git a/.gitignore b/.gitignore
index a9a20e83c..d2d8f18f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,8 @@
# Package Manager
## cargo
target
+# nix
+shell.nix
## npm
node_modules
diff --git a/Cargo.lock b/Cargo.lock
index 52eefb2bf..44f5562d3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -233,6 +233,124 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
+[[package]]
+name = "ark-bls12-381"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-ec"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c60370a92f8e1a5f053cad73a862e1b99bc642333cd676fa11c0c39f80f4ac2"
+dependencies = [
+ "ark-ff",
+ "ark-poly",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "hashbrown 0.13.2",
+ "itertools",
+ "num-traits",
+ "zeroize",
+]
+
+[[package]]
+name = "ark-ff"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c2d42532524bee1da5a4f6f733eb4907301baa480829557adcff5dfaeee1d9a"
+dependencies = [
+ "ark-ff-asm",
+ "ark-ff-macros",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "digest 0.10.6",
+ "itertools",
+ "num-bigint",
+ "num-traits",
+ "paste",
+ "rustc_version",
+ "zeroize",
+]
+
+[[package]]
+name = "ark-ff-asm"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d6873aaba7959593d89babed381d33e2329453368f1bf3c67e07686a1c1056f"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ark-ff-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3c2e7d0f2d67cc7fc925355c74d36e7eda19073639be4a0a233d4611b8c959d"
+dependencies = [
+ "num-bigint",
+ "num-traits",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ark-poly"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f6ec811462cabe265cfe1b102fcfe3df79d7d2929c2425673648ee9abfd0272"
+dependencies = [
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "hashbrown 0.13.2",
+]
+
+[[package]]
+name = "ark-serialize"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7e735959bc173ea4baf13327b19c22d452b8e9e8e8f7b7fc34e6bf0e316c33e"
+dependencies = [
+ "ark-serialize-derive",
+ "ark-std",
+ "digest 0.10.6",
+ "num-bigint",
+]
+
+[[package]]
+name = "ark-serialize-derive"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd34f0920d995d2c932f38861c416f70de89a6de9875876b012557079603e6cc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ark-std"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185"
+dependencies = [
+ "num-traits",
+ "rand 0.8.5",
+]
+
[[package]]
name = "array-bytes"
version = "4.2.0"
@@ -2531,17 +2649,16 @@ dependencies = [
name = "darwinia-precompile-bls12-381"
version = "6.0.0-1"
dependencies = [
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
"fp-evm",
- "frame-system",
- "pallet-balances",
"pallet-evm",
- "pallet-timestamp",
- "parity-scale-codec",
"precompile-utils",
- "scale-info",
- "sp-core",
- "sp-io",
- "sp-runtime",
+ "rand 0.8.5",
+ "sha2 0.10.6",
"sp-std",
]
@@ -4424,6 +4541,9 @@ name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash 0.8.3",
+]
[[package]]
name = "heck"
diff --git a/precompile/bls12-381/Cargo.toml b/precompile/bls12-381/Cargo.toml
index 1bbb86b3e..423b45c1a 100644
--- a/precompile/bls12-381/Cargo.toml
+++ b/precompile/bls12-381/Cargo.toml
@@ -1,12 +1,19 @@
[package]
authors.workspace = true
-description = "BLS12-381 implementation for EVM pallet."
+description = "Arkworks BLS12-381 based precompile for EVM pallet."
edition.workspace = true
name = "darwinia-precompile-bls12-381"
readme = "README.md"
version.workspace = true
[dependencies]
+# crates.io
+ark-bls12-381 = { version = "0.4.0", default-features = false, features = ["curve"] }
+ark-ec = { version = "0.4.1", default-features = false }
+ark-ff = { version = "0.4.1", default-features = false }
+ark-serialize = { version = "0.4.1", default-features = false, features = ["derive"] }
+sha2 = { version = "0.10.6", default-features = false }
+
# frontier
fp-evm = { workspace = true }
pallet-evm = { workspace = true }
@@ -18,27 +25,22 @@ precompile-utils = { workspace = true }
sp-std = { workspace = true }
[dev-dependencies]
-# crates.io
-codec = { package = "parity-scale-codec", workspace = true }
-scale-info = { workspace = true }
-
-# moonbeam
-precompile-utils = { workspace = true, features = ["testing"] }
-
-# substrate
-frame-system = { workspace = true }
-pallet-balances = { workspace = true, features = ["std"] }
-pallet-timestamp = { workspace = true, features = ["std"] }
-sp-core = { workspace = true }
-sp-io = { workspace = true }
-sp-runtime = { workspace = true }
+rand = { version = "0.8.5" }
+ark-std = { version = "0.4.0" }
[features]
default = ["std"]
std = [
+ # crates.io
+ "ark-bls12-381/std",
+ "ark-serialize/std",
+ "ark-ec/std",
+ "ark-ff/std",
+ "sha2/std",
+
# frontier
- "fp-evm/std",
"pallet-evm/std",
+ "fp-evm/std",
# moonbeam
"precompile-utils/std",
diff --git a/precompile/bls12-381/src/bls/mod.rs b/precompile/bls12-381/src/bls/mod.rs
new file mode 100644
index 000000000..2566f3007
--- /dev/null
+++ b/precompile/bls12-381/src/bls/mod.rs
@@ -0,0 +1,180 @@
+// This file is part of Darwinia.
+//
+// Copyright (C) 2018-2023 Darwinia Network
+// SPDX-License-Identifier: GPL-3.0
+//
+// Darwinia is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Darwinia is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Darwinia. If not, see .
+//
+// Inspired from https://github.com/w3f/apk-proofs/blob/main/bw6/src/bls/mod.rs
+
+// core
+use core::{borrow::Borrow, ops::Neg};
+// crates.io
+use ark_bls12_381::{
+ g2::Config as G2Config, Bls12_381, G1Affine, G1Projective, G2Affine, G2Projective,
+};
+use ark_ec::{
+ hashing::{
+ curve_maps::wb::WBMap, map_to_curve_hasher::MapToCurveBasedHasher, HashToCurve,
+ HashToCurveError,
+ },
+ models::short_weierstrass::Projective,
+ pairing::Pairing,
+ AffineRepr, CurveGroup,
+};
+use ark_ff::{field_hashers::DefaultFieldHasher, Zero};
+use ark_serialize::*;
+use sha2::Sha256;
+// substrate
+use sp_std::prelude::Vec;
+
+/// Domain Separation Tag for signatures on G2
+pub const DST_G2: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
+
+#[derive(Clone, Debug)]
+pub struct Signature(G2Projective);
+impl From for Signature {
+ fn from(sig: G2Projective) -> Signature {
+ Signature(sig)
+ }
+}
+impl AsRef for Signature {
+ fn as_ref(&self) -> &G2Projective {
+ &self.0
+ }
+}
+impl Signature {
+ pub fn from_bytes(bytes: &[u8]) -> Result {
+ let p = G2Affine::deserialize_compressed(bytes)?;
+ Ok(Self(p.into()))
+ }
+
+ #[allow(dead_code)]
+ pub fn aggregate>(signatures: impl IntoIterator- ) -> Signature {
+ signatures.into_iter().map(|s| s.borrow().0).sum::().into()
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash, CanonicalSerialize, CanonicalDeserialize)]
+pub struct PublicKey(pub G1Projective);
+impl From for PublicKey {
+ fn from(pk: G1Projective) -> PublicKey {
+ PublicKey(pk)
+ }
+}
+impl PublicKey {
+ pub fn from_bytes(bytes: &[u8]) -> Result {
+ let p = G1Affine::deserialize_compressed(bytes)?;
+ Ok(Self(p.into()))
+ }
+
+ pub fn aggregate>(public_keys: impl IntoIterator
- ) -> PublicKey {
+ public_keys.into_iter().map(|s| s.borrow().0).sum::().into()
+ }
+
+ pub fn verify(&self, signature: &Signature, message: &G2Projective) -> bool {
+ Bls12_381::multi_pairing(
+ [G1Affine::generator().neg(), self.0.into_affine()],
+ [signature.as_ref().into_affine(), message.into_affine()],
+ )
+ .is_zero()
+ }
+}
+
+pub fn hash_to_curve_g2(message: &[u8]) -> Result {
+ let wb_to_curve_hasher = MapToCurveBasedHasher::<
+ Projective,
+ DefaultFieldHasher,
+ WBMap,
+ >::new(DST_G2)?;
+ Ok(wb_to_curve_hasher.hash(message)?.into())
+}
+
+#[cfg(test)]
+mod tests {
+ // crates.io
+ use rand::Rng;
+ use ark_std::test_rng;
+ use ark_bls12_381::Fr;
+ use ark_ec::Group;
+ use ark_ff::UniformRand;
+ // darwinia
+ use super::*;
+
+ #[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)]
+ pub struct SecretKey(Fr);
+
+ impl From for SecretKey {
+ fn from(sk: Fr) -> SecretKey {
+ SecretKey(sk)
+ }
+ }
+
+ impl From<&SecretKey> for PublicKey {
+ fn from(sk: &SecretKey) -> PublicKey {
+ (G1Projective::generator() * sk.as_ref()).into()
+ }
+ }
+
+ impl AsRef for SecretKey {
+ fn as_ref(&self) -> &Fr {
+ &self.0
+ }
+ }
+
+ impl SecretKey {
+ pub fn new(rng: &mut R) -> SecretKey {
+ SecretKey(Fr::rand(rng))
+ }
+
+ pub fn sign(&self, message: &G2Projective) -> Signature {
+ (*message * self.as_ref()).into()
+ }
+ }
+
+ #[test]
+ fn test_apk() {
+ let rng = &mut test_rng();
+ let message = G2Projective::rand(rng);
+
+ let sks = (0..10).map(|_| SecretKey::new(rng)).collect::>();
+ let pks = sks.iter().map(PublicKey::from).collect::>();
+ let sigs = sks.iter().map(|sk| sk.sign(&message)).collect::>();
+ pks.iter().zip(sigs.iter()).for_each(|(pk, sig)| assert!(pk.verify(sig, &message)));
+
+ let apk = PublicKey::aggregate(pks);
+ let asig = Signature::aggregate(sigs);
+ assert!(apk.verify(&asig, &message));
+ }
+
+ #[test]
+ fn test_h2c() {
+ let message = vec![
+ 58, 137, 108, 164, 181, 219, 16, 43, 157, 253, 71, 82, 139, 6, 34, 10, 145, 189, 18,
+ 70, 29, 204, 134, 121, 60, 226, 213, 145, 244, 30, 164, 248,
+ ];
+ let e = vec![
+ 178, 18, 44, 225, 215, 170, 68, 228, 52, 151, 40, 113, 171, 202, 76, 203, 156, 112,
+ 105, 249, 147, 210, 132, 79, 69, 117, 109, 151, 35, 71, 117, 21, 119, 179, 181, 81, 92,
+ 22, 22, 88, 190, 243, 147, 248, 3, 210, 87, 98, 0, 84, 201, 248, 182, 249, 99, 59, 86,
+ 60, 71, 244, 250, 189, 134, 232, 18, 82, 72, 76, 83, 155, 46, 113, 128, 107, 49, 67,
+ 174, 100, 244, 181, 33, 174, 14, 151, 112, 62, 141, 100, 173, 191, 103, 178, 205, 17,
+ 237, 147,
+ ];
+ let p: G2Affine = hash_to_curve_g2(&message).unwrap().into();
+ let mut c = Vec::new();
+ p.serialize_compressed(&mut c).unwrap();
+ assert_eq!(e, c);
+ }
+}
diff --git a/precompile/bls12-381/src/lib.rs b/precompile/bls12-381/src/lib.rs
index 5dc2a39c9..12697e1b8 100644
--- a/precompile/bls12-381/src/lib.rs
+++ b/precompile/bls12-381/src/lib.rs
@@ -18,6 +18,9 @@
#![cfg_attr(not(feature = "std"), no_std)]
+mod bls;
+use bls::{hash_to_curve_g2, PublicKey, Signature};
+
// core
use core::marker::PhantomData;
// moonbeam
@@ -25,18 +28,31 @@ use precompile_utils::prelude::*;
// substrate
use sp_std::prelude::*;
+pub(crate) const BLS_ESTIMATED_COST: u64 = 100_000;
pub struct BLS12381(PhantomData);
#[precompile_utils::precompile]
impl BLS12381 {
#[precompile::public("fast_aggregate_verify(bytes[],bytes,bytes)")]
#[precompile::view]
- fn state_storage_at(
- _handle: &mut impl PrecompileHandle,
- _pubkeys: Vec,
- _message: UnboundedBytes,
- _signature: UnboundedBytes,
+ fn fast_aggregate_verify(
+ handle: &mut impl PrecompileHandle,
+ pubkeys: Vec,
+ message: UnboundedBytes,
+ signature: UnboundedBytes,
) -> EvmResult {
- Err(revert("Unavailable now"))
+ handle.record_cost(BLS_ESTIMATED_COST)?;
+
+ let asig =
+ Signature::from_bytes(signature.as_bytes()).map_err(|_| revert("Invalid signature"))?;
+ let public_keys: Result, _> =
+ pubkeys.into_iter().map(|k| PublicKey::from_bytes(k.as_bytes())).collect();
+ let Ok(pks) = public_keys else {
+ return Err(revert("Invalid pubkeys"));
+ };
+
+ let apk = PublicKey::aggregate(pks);
+ let msg = hash_to_curve_g2(message.as_bytes()).map_err(|_| revert("Invalid message"))?;
+ Ok(apk.verify(&asig, &msg))
}
}
diff --git a/runtime/crab/src/pallets/evm.rs b/runtime/crab/src/pallets/evm.rs
index 2e28a9ff1..950984e3b 100644
--- a/runtime/crab/src/pallets/evm.rs
+++ b/runtime/crab/src/pallets/evm.rs
@@ -49,7 +49,7 @@ where
Self(Default::default())
}
- pub fn used_addresses() -> [sp_core::H160; 15] {
+ pub fn used_addresses() -> [sp_core::H160; 14] {
[
addr(1),
addr(2),
@@ -66,7 +66,7 @@ where
addr(1026),
addr(1536),
addr(1537),
- addr(2048),
+ // addr(2048),
]
}
}
@@ -118,8 +118,8 @@ where
a if a == addr(1537) =>
Some(>::execute(handle)),
// [2048..) reserved for the experimental precompiles.
- a if a == addr(2048) =>
- Some(>::execute(handle)),
+ // a if a == addr(2048) =>
+ // Some(>::execute(handle)),
_ => None,
}
}
diff --git a/runtime/darwinia/src/pallets/evm.rs b/runtime/darwinia/src/pallets/evm.rs
index 49ba628dd..258dde402 100644
--- a/runtime/darwinia/src/pallets/evm.rs
+++ b/runtime/darwinia/src/pallets/evm.rs
@@ -49,7 +49,7 @@ where
Self(Default::default())
}
- pub fn used_addresses() -> [sp_core::H160; 15] {
+ pub fn used_addresses() -> [sp_core::H160; 14] {
[
addr(1),
addr(2),
@@ -66,7 +66,7 @@ where
addr(1026),
addr(1536),
addr(1537),
- addr(2048),
+ // addr(2048),
]
}
}
@@ -118,8 +118,8 @@ where
a if a == addr(1537) =>
Some(>::execute(handle)),
// [2048..) reserved for the experimental precompiles.
- a if a == addr(2048) =>
- Some(>::execute(handle)),
+ // a if a == addr(2048) =>
+ // Some(>::execute(handle)),
_ => None,
}
}