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, } }