diff --git a/Cargo.lock b/Cargo.lock
index 4d8ee2ceffb..4cf4ec8b0ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -262,6 +262,29 @@ dependencies = [
"derive_arbitrary",
]
+[[package]]
+name = "ark-bls12-377"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-bls12-377-ext"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20c7021f180a0cbea0380eba97c2af3c57074cdaffe0eef7e840e1c9f2841e55"
+dependencies = [
+ "ark-bls12-377",
+ "ark-ec",
+ "ark-models-ext",
+ "ark-std",
+]
+
[[package]]
name = "ark-bls12-381"
version = "0.4.0"
@@ -274,6 +297,45 @@ dependencies = [
"ark-std",
]
+[[package]]
+name = "ark-bls12-381-ext"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1dc4b3d08f19e8ec06e949712f95b8361e43f1391d94f65e4234df03480631c"
+dependencies = [
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-models-ext",
+ "ark-serialize",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-bw6-761"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e0605daf0cc5aa2034b78d008aaf159f56901d92a52ee4f6ecdfdac4f426700"
+dependencies = [
+ "ark-bls12-377",
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-bw6-761-ext"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccee5fba47266f460067588ee1bf070a9c760bf2050c1c509982c5719aadb4f2"
+dependencies = [
+ "ark-bw6-761",
+ "ark-ec",
+ "ark-ff",
+ "ark-models-ext",
+ "ark-std",
+]
+
[[package]]
name = "ark-ec"
version = "0.4.2"
@@ -288,9 +350,35 @@ dependencies = [
"hashbrown 0.13.2",
"itertools",
"num-traits",
+ "rayon",
"zeroize",
]
+[[package]]
+name = "ark-ed-on-bls12-377"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b10d901b9ac4b38f9c32beacedfadcdd64e46f8d7f8e88c1ae1060022cf6f6c6"
+dependencies = [
+ "ark-bls12-377",
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-ed-on-bls12-377-ext"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524a4fb7540df2e1a8c2e67a83ba1d1e6c3947f4f9342cc2359fc2e789ad731d"
+dependencies = [
+ "ark-ec",
+ "ark-ed-on-bls12-377",
+ "ark-ff",
+ "ark-models-ext",
+ "ark-std",
+]
+
[[package]]
name = "ark-ed-on-bls12-381-bandersnatch"
version = "0.4.0"
@@ -303,6 +391,19 @@ dependencies = [
"ark-std",
]
+[[package]]
+name = "ark-ed-on-bls12-381-bandersnatch-ext"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d15185f1acb49a07ff8cbe5f11a1adc5a93b19e211e325d826ae98e98e124346"
+dependencies = [
+ "ark-ec",
+ "ark-ed-on-bls12-381-bandersnatch",
+ "ark-ff",
+ "ark-models-ext",
+ "ark-std",
+]
+
[[package]]
name = "ark-ff"
version = "0.4.2"
@@ -346,6 +447,19 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "ark-models-ext"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e9eab5d4b5ff2f228b763d38442adc9b084b0a465409b059fac5c2308835ec2"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+]
+
[[package]]
name = "ark-poly"
version = "0.4.2"
@@ -432,6 +546,7 @@ checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185"
dependencies = [
"num-traits",
"rand 0.8.5",
+ "rayon",
]
[[package]]
@@ -2196,6 +2311,17 @@ dependencies = [
"parity-scale-codec",
]
+[[package]]
+name = "demo-bls381"
+version = "0.1.0"
+dependencies = [
+ "gbuiltin-bls381",
+ "gear-wasm-builder",
+ "gstd",
+ "hex-literal",
+ "parity-scale-codec",
+]
+
[[package]]
name = "demo-calc-hash"
version = "0.1.0"
@@ -4011,6 +4137,19 @@ dependencies = [
"gear-dlmalloc",
]
+[[package]]
+name = "gbuiltin-bls381"
+version = "1.2.0"
+dependencies = [
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-scale 0.0.12",
+ "ark-serialize",
+ "derive_more",
+ "parity-scale-codec",
+]
+
[[package]]
name = "gcli"
version = "1.2.0"
@@ -4059,8 +4198,15 @@ name = "gclient"
version = "1.2.0"
dependencies = [
"anyhow",
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-scale 0.0.12",
+ "ark-serialize",
+ "ark-std",
"async-trait",
"demo-async-tester",
+ "demo-bls381",
"demo-calc-hash",
"demo-calc-hash-in-one-block",
"demo-constructor",
@@ -4437,6 +4583,7 @@ dependencies = [
"sp-blockchain",
"sp-consensus",
"sp-core",
+ "sp-crypto-ec-utils",
"sp-inherents",
"sp-io",
"sp-keyring",
@@ -4643,6 +4790,7 @@ dependencies = [
"sp-consensus-babe",
"sp-consensus-grandpa",
"sp-core",
+ "sp-crypto-ec-utils",
"sp-keystore",
"sp-offchain",
"sp-runtime",
@@ -7877,6 +8025,12 @@ dependencies = [
name = "pallet-gear-builtin"
version = "1.2.0"
dependencies = [
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-scale 0.0.12",
+ "ark-serialize",
+ "ark-std",
"demo-waiting-proxy",
"derive_more",
"env_logger",
@@ -7885,6 +8039,7 @@ dependencies = [
"frame-support",
"frame-support-test",
"frame-system",
+ "gbuiltin-bls381",
"gear-common",
"gear-core",
"gear-core-errors",
@@ -7905,6 +8060,7 @@ dependencies = [
"primitive-types",
"scale-info",
"sp-core",
+ "sp-crypto-ec-utils",
"sp-externalities",
"sp-io",
"sp-runtime",
@@ -12044,6 +12200,27 @@ dependencies = [
"syn 2.0.53",
]
+[[package]]
+name = "sp-crypto-ec-utils"
+version = "0.4.1"
+source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.1.0#2dd122c65e27d0d09d84e1533e14565cf179aec1"
+dependencies = [
+ "ark-bls12-377",
+ "ark-bls12-377-ext",
+ "ark-bls12-381",
+ "ark-bls12-381-ext",
+ "ark-bw6-761",
+ "ark-bw6-761-ext",
+ "ark-ec",
+ "ark-ed-on-bls12-377",
+ "ark-ed-on-bls12-377-ext",
+ "ark-ed-on-bls12-381-bandersnatch",
+ "ark-ed-on-bls12-381-bandersnatch-ext",
+ "ark-scale 0.0.12",
+ "sp-runtime-interface",
+ "sp-std 8.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-v1.1.0)",
+]
+
[[package]]
name = "sp-database"
version = "4.0.0-dev"
diff --git a/Cargo.toml b/Cargo.toml
index f7ffd42a579..07a1439f84c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,6 +26,7 @@ members = [
"examples/async-signal-entry",
"examples/async-tester",
"examples/autoreply",
+ "examples/bls381",
"examples/calc-hash",
"examples/custom",
"examples/delayed-reservation-sender",
@@ -72,6 +73,7 @@ members = [
"examples/waiting-proxy",
"examples/wat",
"galloc",
+ "gbuiltins/bls381",
"gcli",
"gclient",
"gcore",
@@ -166,6 +168,12 @@ which = "4.4.2"
winapi = "0.3.9"
paste = "1.0"
tempfile = "3.10.1"
+ark-std = { version = "0.4.0", default-features = false }
+ark-bls12-381 = { version = "0.4.0", default-features = false }
+ark-serialize = { version = "0.4", default-features = false }
+ark-ec = { version = "0.4.2", default-features = false }
+ark-ff = { version = "0.4.2", default-features = false }
+ark-scale = { version = "0.0.12", default-features = false }
# Published deps
#
@@ -182,6 +190,7 @@ authorship = { package = "gear-authorship", path = "node/authorship" }
common = { package = "gear-common", path = "common", default-features = false }
core-processor = { package = "gear-core-processor", path = "core-processor", default-features = false }
galloc = { path = "galloc" }
+gbuiltin-bls381 = { path = "gbuiltins/bls381", default-features = false }
gcore = { path = "gcore" }
gcli = { path = "gcli" }
gclient = { path = "gclient" }
@@ -304,6 +313,7 @@ sc-consensus-babe = { version = "0.10.0-dev", git = "https://github.com/gear-tec
sc-consensus-babe-rpc = { version = "0.10.0-dev", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0" }
sc-consensus-epochs = { version = "0.10.0-dev", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0" }
sc-consensus-slots = { version = "0.10.0-dev", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0" }
+sp-crypto-ec-utils = { version = "0.4.1", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0", default-features = false }
sp-debug-derive = { version = "8.0.0", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0", default-features = false }
sc-chain-spec = { version = "4.0.0-dev", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0" }
sc-cli = { version = "0.10.0-dev", git = "https://github.com/gear-tech/polkadot-sdk.git", branch = "gear-v1.1.0" }
@@ -379,6 +389,7 @@ demo-async-init = { path = "examples/async-init" }
demo-async-recursion = { path = "examples/async-recursion" }
demo-async-signal-entry = { path = "examples/async-signal-entry" }
demo-async-tester = { path = "examples/async-tester" }
+demo-bls381 = { path = "examples/bls381" }
demo-calc-hash = { path = "examples/calc-hash" }
demo-calc-hash-in-one-block = { path = "examples/calc-hash/in-one-block" }
demo-calc-hash-over-blocks = { path = "examples/calc-hash/over-blocks" }
diff --git a/examples/bls381/Cargo.toml b/examples/bls381/Cargo.toml
new file mode 100644
index 00000000000..6a6bd9b57ef
--- /dev/null
+++ b/examples/bls381/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "demo-bls381"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+codec = { workspace = true, features = ["derive"] }
+gstd = { workspace = true, features = ["debug"] }
+gbuiltin-bls381.workspace = true
+hex-literal.workspace = true
+
+[build-dependencies]
+gear-wasm-builder.workspace = true
+
+[features]
+default = ["std"]
+std = ["codec/std"]
diff --git a/examples/bls381/build.rs b/examples/bls381/build.rs
new file mode 100644
index 00000000000..6a370b53a71
--- /dev/null
+++ b/examples/bls381/build.rs
@@ -0,0 +1,21 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+fn main() {
+ gear_wasm_builder::build();
+}
diff --git a/examples/bls381/src/lib.rs b/examples/bls381/src/lib.rs
new file mode 100644
index 00000000000..b982a3ceefe
--- /dev/null
+++ b/examples/bls381/src/lib.rs
@@ -0,0 +1,52 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+use codec::{Decode, Encode};
+
+#[cfg(feature = "std")]
+mod code {
+ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
+}
+
+#[cfg(feature = "std")]
+pub use code::WASM_BINARY_OPT as WASM_BINARY;
+
+#[derive(Encode, Decode)]
+#[codec(crate = codec)]
+pub enum HandleMessage {
+ MillerLoop {
+ message: Vec,
+ signatures: Vec>,
+ },
+ Exp,
+}
+
+#[derive(Encode, Decode)]
+#[codec(crate = codec)]
+pub struct InitMessage {
+ pub g2_gen: Vec,
+ pub pub_keys: Vec>,
+}
+
+#[cfg(not(feature = "std"))]
+mod wasm;
diff --git a/examples/bls381/src/wasm.rs b/examples/bls381/src/wasm.rs
new file mode 100644
index 00000000000..ca8e74b0f9b
--- /dev/null
+++ b/examples/bls381/src/wasm.rs
@@ -0,0 +1,182 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+use super::*;
+use ark_bls12_381::{Bls12_381, G1Affine, G2Affine};
+use ark_ec::pairing::Pairing;
+use gbuiltin_bls381::*;
+use gstd::{
+ codec::{Decode, Encode},
+ msg,
+ prelude::*,
+ ActorId,
+};
+use hex_literal::hex;
+
+type ArkScale = ark_scale::ArkScale;
+
+const BUILTIN_BLS381: ActorId = ActorId::new(hex!(
+ "6b6e292c382945e80bf51af2ba7fe9f458dcff81ae6075c46f9095e1bbecdc37"
+));
+
+#[allow(dead_code)]
+#[derive(Default)]
+pub struct Contract {
+ g2_gen: G2Affine,
+ pub_keys: Vec,
+ aggregate_pub_key: G2Affine,
+ miller_out: (
+ // encoded ArkScale::>
+ Option>,
+ Option>,
+ ),
+}
+
+static mut CONTRACT: Option = None;
+
+#[no_mangle]
+extern "C" fn init() {
+ let init_msg: InitMessage = msg::load().expect("Unable to decode `InitMessage`");
+
+ let g2_gen = ::G2Affine> as Decode>::decode(
+ &mut init_msg.g2_gen.as_slice(),
+ )
+ .unwrap();
+
+ let mut pub_keys = Vec::new();
+ let mut aggregate_pub_key: G2Affine = Default::default();
+
+ for pub_key_bytes in init_msg.pub_keys.iter() {
+ let pub_key = ::G2Affine> as Decode>::decode(
+ &mut pub_key_bytes.as_slice(),
+ )
+ .unwrap();
+ aggregate_pub_key = (aggregate_pub_key + pub_key.0).into();
+ pub_keys.push(pub_key.0);
+ }
+
+ let contract = Contract {
+ g2_gen: g2_gen.0,
+ pub_keys,
+ aggregate_pub_key,
+ miller_out: (None, None),
+ };
+
+ unsafe { CONTRACT = Some(contract) }
+}
+
+#[gstd::async_main]
+async fn main() {
+ let msg: HandleMessage = msg::load().expect("Unable to decode `HandleMessage`");
+ let contract = unsafe { CONTRACT.as_mut().expect("The contract is not initialized") };
+
+ match msg {
+ HandleMessage::MillerLoop {
+ message,
+ signatures,
+ } => {
+ let aggregate_pub_key: ArkScale> =
+ vec![contract.aggregate_pub_key].into();
+
+ let request = Request::MultiMillerLoop {
+ a: message,
+ b: aggregate_pub_key.encode(),
+ }
+ .encode();
+ let reply = msg::send_bytes_for_reply(BUILTIN_BLS381, &request, 0, 0)
+ .expect("Failed to send message")
+ .await
+ .expect("Received error reply");
+
+ let response = Response::decode(&mut reply.as_slice()).unwrap();
+ let miller_out1 = match response {
+ Response::MultiMillerLoop(v) => v,
+ _ => unreachable!(),
+ };
+
+ let mut aggregate_signature: G1Affine = Default::default();
+ for signature in signatures.iter() {
+ let signature = ::G1Affine> as Decode>::decode(
+ &mut signature.as_slice(),
+ )
+ .unwrap();
+ aggregate_signature = (aggregate_signature + signature.0).into();
+ }
+ let aggregate_signature: ArkScale> = vec![aggregate_signature].into();
+ let g2_gen: ArkScale> = vec![contract.g2_gen].into();
+ let request = Request::MultiMillerLoop {
+ a: aggregate_signature.encode(),
+ b: g2_gen.encode(),
+ }
+ .encode();
+ let reply = msg::send_bytes_for_reply(BUILTIN_BLS381, &request, 0, 0)
+ .expect("Failed to send message")
+ .await
+ .expect("Received error reply");
+ let response = Response::decode(&mut reply.as_slice()).unwrap();
+ let miller_out2 = match response {
+ Response::MultiMillerLoop(v) => v,
+ _ => unreachable!(),
+ };
+
+ contract.miller_out = (Some(miller_out1), Some(miller_out2));
+ }
+
+ HandleMessage::Exp => {
+ if let (Some(miller_out1), Some(miller_out2)) = &contract.miller_out {
+ let request = Request::FinalExponentiation {
+ f: miller_out1.clone(),
+ }
+ .encode();
+ let reply = msg::send_bytes_for_reply(BUILTIN_BLS381, &request, 0, 0)
+ .expect("Failed to send message")
+ .await
+ .expect("Received error reply");
+ let response = Response::decode(&mut reply.as_slice()).unwrap();
+ let exp1 = match response {
+ Response::FinalExponentiation(v) => {
+ ArkScale::<::TargetField>::decode(&mut v.as_slice())
+ .unwrap()
+ }
+ _ => unreachable!(),
+ };
+
+ let request = Request::FinalExponentiation {
+ f: miller_out2.clone(),
+ }
+ .encode();
+ let reply = msg::send_bytes_for_reply(BUILTIN_BLS381, &request, 0, 0)
+ .expect("Failed to send message")
+ .await
+ .expect("Received error reply");
+ let response = Response::decode(&mut reply.as_slice()).unwrap();
+ let exp2 = match response {
+ Response::FinalExponentiation(v) => {
+ ArkScale::<::TargetField>::decode(&mut v.as_slice())
+ .unwrap()
+ }
+ _ => unreachable!(),
+ };
+
+ assert_eq!(exp1.0, exp2.0);
+
+ contract.miller_out = (None, None);
+ }
+ }
+ }
+}
diff --git a/gbuiltins/bls381/Cargo.toml b/gbuiltins/bls381/Cargo.toml
new file mode 100644
index 00000000000..7a9f5236a55
--- /dev/null
+++ b/gbuiltins/bls381/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "gbuiltin-bls381"
+description = "Entities for working with Gear builtin actor providing bls12_381 cryptography"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+
+[dependencies]
+codec = { workspace = true, features = ["derive"] }
+derive_more.workspace = true
+ark-bls12-381 = { workspace = true, features = ["curve"] }
+ark-ec.workspace = true
+ark-ff.workspace = true
+ark-scale = { workspace = true, features = ["hazmat"] }
+ark-serialize = { workspace = true, features = ["derive"] }
diff --git a/gbuiltins/bls381/src/lib.rs b/gbuiltins/bls381/src/lib.rs
new file mode 100644
index 00000000000..418db75b11b
--- /dev/null
+++ b/gbuiltins/bls381/src/lib.rs
@@ -0,0 +1,178 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+#![no_std]
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+use codec::{Decode, Encode};
+
+pub use ark_bls12_381;
+pub use ark_ec;
+pub use ark_ff;
+pub use ark_scale;
+pub use ark_serialize;
+
+/// Constant defines codec index of [`Request::MultiMillerLoop`].
+pub const REQUEST_MULTI_MILLER_LOOP: u8 = 0;
+/// Constant defines codec index of [`Request::FinalExponentiation`].
+pub const REQUEST_FINAL_EXPONENTIATION: u8 = 1;
+/// Constant defines codec index of [`Request::MultiScalarMultiplicationG1`].
+pub const REQUEST_MULTI_SCALAR_MULTIPLICATION_G1: u8 = 2;
+/// Constant defines codec index of [`Request::MultiScalarMultiplicationG2`].
+pub const REQUEST_MULTI_SCALAR_MULTIPLICATION_G2: u8 = 3;
+/// Constant defines codec index of [`Request::ProjectiveMultiplicationG1`].
+pub const REQUEST_PROJECTIVE_MULTIPLICATION_G1: u8 = 4;
+/// Constant defines codec index of [`Request::ProjectiveMultiplicationG2`].
+pub const REQUEST_PROJECTIVE_MULTIPLICATION_G2: u8 = 5;
+
+/// Type that should be used to create a message to the bls12_381 builtin actor.
+/// Use the following crates to construct a request:
+/// - `ark-scale`: ;
+/// - `ark-bls12-381`: .
+#[derive(Encode, Clone, PartialEq, Eq, Debug)]
+#[codec(crate = codec)]
+pub enum Request {
+ /// Request to pairing multi Miller loop for *BLS12-381*.
+ ///
+ /// Encoded:
+ /// - `a`: [`ArkScale>`].
+ /// - `b`: [`ArkScale>`].
+ #[codec(index = 0)]
+ MultiMillerLoop { a: Vec, b: Vec },
+
+ /// Request to pairing final exponentiation for *BLS12-381*.
+ ///
+ /// Encoded: [`ArkScale<`].
+ #[codec(index = 1)]
+ FinalExponentiation { f: Vec },
+
+ /// Request to multi scalar multiplication on *G1* for *BLS12-381*
+ ///
+ /// Encoded:
+ /// - `bases`: [`ArkScale>`].
+ /// - `scalars`: [`ArkScale>`].
+ #[codec(index = 2)]
+ MultiScalarMultiplicationG1 { bases: Vec, scalars: Vec },
+
+ /// Request to multi scalar multiplication on *G2* for *BLS12-381*
+ ///
+ /// Encoded:
+ /// - `bases`: [`ArkScale>`].
+ /// - `scalars`: [`ArkScale>`].
+ #[codec(index = 3)]
+ MultiScalarMultiplicationG2 { bases: Vec, scalars: Vec },
+
+ /// Request to projective multiplication on *G1* for *BLS12-381*.
+ ///
+ /// Encoded:
+ /// - `base`: [`ArkScaleProjective`].
+ /// - `scalar`: [`ArkScale>`].
+ #[codec(index = 4)]
+ ProjectiveMultiplicationG1 { base: Vec, scalar: Vec },
+
+ /// Request to projective multiplication on *G2* for *BLS12-381*.
+ ///
+ /// Encoded:
+ /// - `base`: [`ArkScaleProjective`].
+ /// - `scalar`: [`ArkScale>`].
+ #[codec(index = 5)]
+ ProjectiveMultiplicationG2 { base: Vec, scalar: Vec },
+}
+
+/// The enumeration contains result to a request.
+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)]
+#[codec(crate = codec)]
+pub enum Response {
+ /// Result of the multi Miller loop, encoded: [`ArkScale`].
+ MultiMillerLoop(Vec),
+ /// Result of the final exponentiation, encoded: [`ArkScale`].
+ FinalExponentiation(Vec),
+ /// Result of the multi scalar multiplication, encoded: [`ArkScaleProjective`].
+ MultiScalarMultiplicationG1(Vec),
+ /// Result of the multi scalar multiplication, encoded: [`ArkScaleProjective`].
+ MultiScalarMultiplicationG2(Vec),
+ /// Result of the projective multiplication, encoded: [`ArkScaleProjective`].
+ ProjectiveMultiplicationG1(Vec),
+ /// Result of the projective multiplication, encoded: [`ArkScaleProjective`].
+ ProjectiveMultiplicationG2(Vec),
+}
+
+#[cfg(test)]
+mod tests {
+ extern crate alloc;
+
+ use super::*;
+ use alloc::vec;
+
+ // The standard Decode implementation cannot be used for precise gas charging.
+ // The following test checks that scale codec indexes of variants are set correctly.
+ #[test]
+ fn codec_enum_indexes() {
+ for (index, (variant, request)) in [
+ (
+ REQUEST_MULTI_MILLER_LOOP,
+ Request::MultiMillerLoop {
+ a: vec![],
+ b: vec![],
+ },
+ ),
+ (
+ REQUEST_FINAL_EXPONENTIATION,
+ Request::FinalExponentiation { f: vec![] },
+ ),
+ (
+ REQUEST_MULTI_SCALAR_MULTIPLICATION_G1,
+ Request::MultiScalarMultiplicationG1 {
+ bases: vec![],
+ scalars: vec![],
+ },
+ ),
+ (
+ REQUEST_MULTI_SCALAR_MULTIPLICATION_G2,
+ Request::MultiScalarMultiplicationG2 {
+ bases: vec![],
+ scalars: vec![],
+ },
+ ),
+ (
+ REQUEST_PROJECTIVE_MULTIPLICATION_G1,
+ Request::ProjectiveMultiplicationG1 {
+ base: vec![],
+ scalar: vec![],
+ },
+ ),
+ (
+ REQUEST_PROJECTIVE_MULTIPLICATION_G2,
+ Request::ProjectiveMultiplicationG2 {
+ base: vec![],
+ scalar: vec![],
+ },
+ ),
+ ]
+ .into_iter()
+ .enumerate()
+ {
+ assert_eq!(index, variant.into());
+ let encoded = request.encode();
+
+ assert!(matches!(encoded.first().copied(), Some(v) if v == variant));
+ }
+ }
+}
diff --git a/gclient/Cargo.toml b/gclient/Cargo.toml
index daf873f476c..93ae66def02 100644
--- a/gclient/Cargo.toml
+++ b/gclient/Cargo.toml
@@ -47,3 +47,10 @@ demo-reserve-gas = { workspace = true, features = ["std"] }
gmeta = { workspace = true }
gstd = { workspace = true, features = ["debug"] }
demo-wat.workspace = true
+demo-bls381 = { workspace = true, features = ["std"] }
+ark-serialize = { workspace = true, features = ["derive"] }
+ark-scale = { workspace = true, features = ["hazmat"] }
+ark-bls12-381 = { workspace = true, features = ["curve"] }
+ark-ec = { workspace = true }
+ark-ff = { workspace = true }
+ark-std = { workspace = true }
diff --git a/gclient/tests/builtin_bls381.rs b/gclient/tests/builtin_bls381.rs
new file mode 100644
index 00000000000..7b28112e730
--- /dev/null
+++ b/gclient/tests/builtin_bls381.rs
@@ -0,0 +1,146 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+use ark_bls12_381::{G1Affine, G1Projective as G1, G2Affine, G2Projective as G2};
+use ark_ec::Group;
+use ark_serialize::CanonicalSerialize;
+use ark_std::{ops::Mul, UniformRand};
+use demo_bls381::*;
+use gclient::{EventListener, EventProcessor, GearApi, Result};
+use gstd::prelude::*;
+
+type ArkScale = ark_scale::ArkScale;
+type ScalarField = ::ScalarField;
+
+async fn common_upload_program(
+ client: &GearApi,
+ code: Vec,
+ payload: impl Encode,
+) -> Result<([u8; 32], [u8; 32])> {
+ let encoded_payload = payload.encode();
+ let gas_limit = client
+ .calculate_upload_gas(None, code.clone(), encoded_payload, 0, true)
+ .await?
+ .min_limit;
+ println!("init gas {gas_limit:?}");
+ let (message_id, program_id, _) = client
+ .upload_program(
+ code,
+ gclient::now_micros().to_le_bytes(),
+ payload,
+ gas_limit,
+ 0,
+ )
+ .await?;
+
+ Ok((message_id.into(), program_id.into()))
+}
+
+async fn upload_program(
+ client: &GearApi,
+ listener: &mut EventListener,
+ payload: impl Encode,
+) -> Result<[u8; 32]> {
+ let (message_id, program_id) =
+ common_upload_program(client, WASM_BINARY.to_vec(), payload).await?;
+
+ assert!(listener
+ .message_processed(message_id.into())
+ .await?
+ .succeed());
+
+ Ok(program_id)
+}
+
+#[tokio::test]
+async fn builtin_bls381() -> Result<()> {
+ let client = GearApi::dev_from_path("../target/release/gear").await?;
+ let mut listener = client.subscribe().await?;
+
+ let mut rng = ark_std::test_rng();
+
+ let generator: G2 = G2::generator();
+ let message: G1Affine = G1::rand(&mut rng).into();
+ let mut pub_keys = Vec::new();
+ let mut signatures = Vec::new();
+ for _ in 0..2 {
+ let priv_key: ScalarField = UniformRand::rand(&mut rng);
+ let pub_key: G2Affine = generator.mul(priv_key).into();
+ let mut pub_key_bytes = Vec::new();
+ pub_key.serialize_uncompressed(&mut pub_key_bytes).unwrap();
+ pub_keys.push(pub_key_bytes);
+
+ // sign
+ let signature: G1Affine = message.mul(priv_key).into();
+ let mut sig_bytes = Vec::new();
+ signature.serialize_uncompressed(&mut sig_bytes).unwrap();
+ signatures.push(sig_bytes);
+ }
+
+ let mut gen_bytes = Vec::new();
+ generator.serialize_uncompressed(&mut gen_bytes).unwrap();
+
+ let program_id = upload_program(
+ &client,
+ &mut listener,
+ InitMessage {
+ g2_gen: gen_bytes,
+ pub_keys,
+ },
+ )
+ .await?;
+
+ let message: ArkScale> = vec![message].into();
+ let message_bytes = message.encode();
+
+ let payload = HandleMessage::MillerLoop {
+ message: message_bytes,
+ signatures,
+ };
+ let gas_limit = client
+ .calculate_handle_gas(None, program_id.into(), payload.encode(), 0, true)
+ .await?
+ .min_limit;
+ println!("gas_limit {gas_limit:?}");
+
+ let (message_id, _) = client
+ .send_message(program_id.into(), payload, gas_limit, 0)
+ .await?;
+
+ assert!(listener.message_processed(message_id).await?.succeed());
+
+ let gas_limit = client
+ .calculate_handle_gas(
+ None,
+ program_id.into(),
+ HandleMessage::Exp.encode(),
+ 0,
+ true,
+ )
+ .await?
+ .min_limit;
+ println!("gas_limit {gas_limit:?}");
+
+ let (message_id, _) = client
+ .send_message(program_id.into(), HandleMessage::Exp, gas_limit, 0)
+ .await?;
+
+ assert!(listener.message_processed(message_id).await?.succeed());
+
+ Ok(())
+}
diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml
index 54676aacaa6..253641c0712 100644
--- a/node/service/Cargo.toml
+++ b/node/service/Cargo.toml
@@ -64,6 +64,7 @@ sc-sysinfo.workspace = true
# Substrate Primitives
sp-core = { workspace = true, features = ["std"] }
+sp-crypto-ec-utils = { workspace = true, features = ["std", "bls12-381"] }
sp-api = { workspace = true, features = ["std"] }
sp-authority-discovery = { workspace = true, optional = true, features = ["std"] }
sp-consensus.workspace = true
diff --git a/node/service/src/client.rs b/node/service/src/client.rs
index f23e43ad295..b398dca40c1 100644
--- a/node/service/src/client.rs
+++ b/node/service/src/client.rs
@@ -55,12 +55,14 @@ impl sc_executor::NativeExecutionDispatch for VaraExecutorDispatch {
frame_benchmarking::benchmarking::HostFunctions,
gear_ri::gear_ri::HostFunctions,
gear_ri::sandbox::HostFunctions,
+ sp_crypto_ec_utils::bls12_381::host_calls::HostFunctions,
);
/// Otherwise we only use the default Substrate host functions.
#[cfg(not(feature = "runtime-benchmarks"))]
type ExtendHostFunctions = (
gear_ri::gear_ri::HostFunctions,
gear_ri::sandbox::HostFunctions,
+ sp_crypto_ec_utils::bls12_381::host_calls::HostFunctions,
);
fn dispatch(method: &str, data: &[u8]) -> Option> {
diff --git a/node/testing/Cargo.toml b/node/testing/Cargo.toml
index ec58506d22f..a3445314ef4 100644
--- a/node/testing/Cargo.toml
+++ b/node/testing/Cargo.toml
@@ -38,6 +38,7 @@ substrate-test-client.workspace = true
# Substrate Primitives
sp-core.workspace = true
+sp-crypto-ec-utils = { workspace = true, features = ["bls12-381"] }
sp-api.workspace = true
sp-consensus.workspace = true
sp-keyring.workspace = true
@@ -60,6 +61,7 @@ std = [
"pallet-gear-rpc-runtime-api/std",
"gear-runtime-interface/std",
"sp-core/std",
+ "sp-crypto-ec-utils/std",
"sp-api/std",
"sp-runtime/std",
"sp-inherents/std",
diff --git a/node/testing/src/client.rs b/node/testing/src/client.rs
index c44283a419e..b37daea9135 100644
--- a/node/testing/src/client.rs
+++ b/node/testing/src/client.rs
@@ -31,6 +31,7 @@ impl sc_executor::NativeExecutionDispatch for LocalExecutorDispatch {
frame_benchmarking::benchmarking::HostFunctions,
gear_runtime_interface::gear_ri::HostFunctions,
gear_runtime_interface::sandbox::HostFunctions,
+ sp_crypto_ec_utils::bls12_381::host_calls::HostFunctions,
);
fn dispatch(method: &str, data: &[u8]) -> Option> {
diff --git a/pallets/gear-builtin/Cargo.toml b/pallets/gear-builtin/Cargo.toml
index 477f2d23330..9488324136a 100644
--- a/pallets/gear-builtin/Cargo.toml
+++ b/pallets/gear-builtin/Cargo.toml
@@ -19,13 +19,21 @@ primitive-types = { workspace = true, features = ["scale-info"] }
log.workspace = true
derive_more.workspace = true
impl-trait-for-tuples.workspace = true
+ark-serialize = { workspace = true, features = ["derive"] }
+ark-scale = { workspace = true, features = ["hazmat"] }
+ark-bls12-381 = { workspace = true, features = ["curve"], optional = true }
+ark-ec = { workspace = true, optional = true }
+ark-ff = { workspace = true, optional = true }
+ark-std = { workspace = true, optional = true }
core-processor.workspace = true
+gbuiltin-bls381.workspace = true
gear-core.workspace = true
gear-core-errors.workspace = true
frame-support.workspace = true
frame-system.workspace = true
frame-benchmarking = { workspace = true, optional = true }
+sp-crypto-ec-utils = { workspace = true, features = ["bls12-381"] }
sp-std.workspace = true
sp-runtime = { workspace = true, features = ["serde"] }
pallet-gear.workspace = true
@@ -48,6 +56,10 @@ frame-executive = { workspace = true, features = ["std"] }
frame-support-test = { workspace = true, features = ["std"] }
env_logger.workspace = true
hex-literal.workspace = true
+ark-bls12-381.workspace = true
+ark-ec.workspace = true
+ark-ff.workspace = true
+ark-std.workspace = true
[features]
default = ["std"]
@@ -59,12 +71,23 @@ std = [
"pallet-gear/std",
"parity-scale-codec/std",
"scale-info/std",
+ "sp-crypto-ec-utils/std",
"sp-runtime/std",
"sp-std/std",
+ "ark-serialize/std",
+ "ark-scale/std",
+ "ark-bls12-381?/std",
+ "ark-ec?/std",
+ "ark-ff?/std",
+ "ark-std?/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
]
try-runtime = ["frame-support/try-runtime"]
diff --git a/pallets/gear-builtin/src/benchmarking.rs b/pallets/gear-builtin/src/benchmarking.rs
index d04f3471b7d..66005f7973b 100644
--- a/pallets/gear-builtin/src/benchmarking.rs
+++ b/pallets/gear-builtin/src/benchmarking.rs
@@ -21,8 +21,17 @@
#[allow(unused)]
use crate::Pallet as BuiltinActorPallet;
use crate::*;
+use ark_bls12_381::{Bls12_381, G1Affine, G1Projective as G1, G2Affine, G2Projective as G2};
+use ark_ec::{pairing::Pairing, short_weierstrass::SWCurveConfig, Group, ScalarMul};
+use ark_ff::biginteger::BigInt;
+use ark_scale::hazmat::ArkScaleProjective;
+use ark_std::{ops::Mul, UniformRand};
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite};
-use gear_core::message::{Payload, StoredDispatch};
+use gear_core::message::{Payload, StoredDispatch, MAX_PAYLOAD_SIZE};
+use parity_scale_codec::{Compact, Encode, Input};
+
+type ArkScale = ark_scale::ArkScale;
+type ScalarField = ::ScalarField;
macro_rules! impl_builtin_actor {
($name: ident, $id: literal) => {
@@ -81,6 +90,18 @@ pub type BenchmarkingBuiltinActor = (
DummyActor15,
);
+const MAX_BIG_INT: u32 = 100;
+
+fn naive_var_base_msm(bases: &[G::MulBase], scalars: &[G::ScalarField]) -> G {
+ let mut acc = G::zero();
+
+ for (base, scalar) in bases.iter().zip(scalars.iter()) {
+ acc += *base * scalar;
+ }
+
+ acc
+}
+
benchmarks! {
where_clause {
where
@@ -101,6 +122,195 @@ benchmarks! {
} verify {
// No changes in runtime are expected since the actual dispatch doesn't take place.
}
+
+ decode_bytes {
+ let a in 1 .. MAX_PAYLOAD_SIZE as u32;
+
+ let bytes = vec![1u8; a as usize];
+ let encoded = bytes.encode();
+ let mut _decoded = vec![];
+ }: {
+ let mut input = encoded.as_slice();
+ let len = u32::from(Compact::::decode(&mut input).unwrap()) as usize;
+
+ let mut items = vec![0u8; len];
+ let bytes_slice = items.as_mut_slice();
+ input.read(bytes_slice).unwrap();
+
+ _decoded = items;
+ } verify {
+ assert_eq!(bytes, _decoded);
+ }
+
+ bls12_381_multi_miller_loop {
+ let c in 0 .. 100;
+
+ let count = c as usize;
+
+ let mut rng = ark_std::test_rng();
+
+ let messages = (0..count).map(|_| G1::rand(&mut rng).into()).collect::>();
+
+ let message: ArkScale::G1Affine>> = messages.into();
+ let encoded_message = message.encode();
+
+ let pub_keys = {
+ let mut pub_keys = Vec::with_capacity(count);
+ let generator: G2 = G2::generator();
+ for _ in 0..count {
+ let priv_key: ScalarField = UniformRand::rand(&mut rng);
+ let pub_key: G2Affine = generator.mul(priv_key).into();
+ pub_keys.push(pub_key);
+ }
+
+ pub_keys
+ };
+ let pub_key: ArkScale::G2Affine>> = pub_keys.into();
+ let encoded_pub_key = pub_key.encode();
+
+ let mut _result: Result, ()> = Err(());
+ }: {
+ _result = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_multi_miller_loop(
+ encoded_message,
+ encoded_pub_key,
+ );
+ } verify {
+ assert!(_result.is_ok());
+ }
+
+ bls12_381_final_exponentiation {
+ let mut rng = ark_std::test_rng();
+
+ // message
+ let message: G1Affine = G1::rand(&mut rng).into();
+ let message: ArkScale::G1Affine>> = vec![message].into();
+ let encoded_message = message.encode();
+
+ let priv_key: ScalarField = UniformRand::rand(&mut rng);
+ let generator: G2 = G2::generator();
+ let pub_key: G2Affine = generator.mul(priv_key).into();
+ let pub_key: ArkScale::G2Affine>> = vec![pub_key].into();
+ let encoded_pub_key = pub_key.encode();
+
+ let miller_loop = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_multi_miller_loop(
+ encoded_message,
+ encoded_pub_key,
+ ).unwrap();
+
+ let mut _result: Result, ()> = Err(());
+ }: {
+ _result = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_final_exponentiation(miller_loop);
+ } verify {
+ assert!(_result.is_ok());
+ }
+
+ bls12_381_msm_g1 {
+ let c in 1 .. 1_000;
+
+ let count = c as usize;
+
+ let mut rng = ark_std::test_rng();
+
+ let scalar = (0..count)
+ .map(|_| ::ScalarField::rand(&mut rng))
+ .max()
+ .unwrap();
+ let scalars = vec![scalar; count];
+ let ark_scalars: ArkScale::ScalarField>> = scalars.clone().into();
+ let encoded_scalars = ark_scalars.encode();
+
+ let bases = (0..count).map(|_| G1::rand(&mut rng)).collect::>();
+ let bases = G1::batch_convert_to_mul_base(&bases);
+ let ark_bases: ArkScale> = bases.clone().into();
+ let encoded_bases = ark_bases.encode();
+
+ let mut _result: Result, ()> = Err(());
+ }: {
+ _result = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_msm_g1(encoded_bases, encoded_scalars);
+ } verify {
+ let naive = naive_var_base_msm::(bases.as_slice(), scalars.as_slice());
+ let encoded = _result.unwrap();
+ let fast = ArkScaleProjective::::decode(&mut &encoded[..]).unwrap();
+ assert_eq!(naive, fast.0);
+ }
+
+ bls12_381_msm_g2 {
+ let c in 1 .. 1_000;
+
+ let count = c as usize;
+
+ let mut rng = ark_std::test_rng();
+
+ let scalar = (0..count)
+ .map(|_| ::ScalarField::rand(&mut rng))
+ .max()
+ .unwrap();
+ let scalars = vec![scalar; count];
+ let ark_scalars: ArkScale::ScalarField>> = scalars.clone().into();
+ let encoded_scalars = ark_scalars.encode();
+
+ let bases = (0..count).map(|_| G2::rand(&mut rng)).collect::>();
+ let bases = G2::batch_convert_to_mul_base(&bases);
+ let ark_bases: ArkScale> = bases.clone().into();
+ let encoded_bases = ark_bases.encode();
+
+ let mut _result: Result, ()> = Err(());
+ }: {
+ _result = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_msm_g2(encoded_bases, encoded_scalars);
+ } verify {
+ let naive = naive_var_base_msm::(bases.as_slice(), scalars.as_slice());
+ let encoded = _result.unwrap();
+ let fast = ArkScaleProjective::::decode(&mut &encoded[..]).unwrap();
+ assert_eq!(naive, fast.0);
+ }
+
+ bls12_381_mul_projective_g1 {
+ let c in 1 .. MAX_BIG_INT;
+
+ let mut rng = ark_std::test_rng();
+
+ let bigint = BigInt::<{ MAX_BIG_INT as usize }>::rand(&mut rng);
+ let bigint = bigint.as_ref()[..c as usize].to_vec();
+ let ark_bigint: ArkScale> = bigint.clone().into();
+ let encoded_bigint = ark_bigint.encode();
+
+ let base = G1::rand(&mut rng);
+ let ark_base: ArkScaleProjective = base.into();
+ let encoded_base = ark_base.encode();
+
+ let mut _result: Result, ()> = Err(());
+ }: {
+ _result = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_mul_projective_g1(encoded_base, encoded_bigint);
+ } verify {
+ let encoded = _result.unwrap();
+ let result = ArkScaleProjective::::decode(&mut &encoded[..]).unwrap();
+ let standard = ::mul_projective(&base, &bigint);
+ assert_eq!(standard, result.0);
+ }
+
+ bls12_381_mul_projective_g2 {
+ let c in 1 .. MAX_BIG_INT;
+
+ let mut rng = ark_std::test_rng();
+
+ let bigint = BigInt::<{ MAX_BIG_INT as usize }>::rand(&mut rng);
+ let bigint = bigint.as_ref()[..c as usize].to_vec();
+ let ark_bigint: ArkScale> = bigint.clone().into();
+ let encoded_bigint = ark_bigint.encode();
+
+ let base = G2::rand(&mut rng);
+ let ark_base: ArkScaleProjective = base.into();
+ let encoded_base = ark_base.encode();
+
+ let mut _result: Result, ()> = Err(());
+ }: {
+ _result = sp_crypto_ec_utils::bls12_381::host_calls::bls12_381_mul_projective_g2(encoded_base, encoded_bigint);
+ } verify {
+ let encoded = _result.unwrap();
+ let result = ArkScaleProjective::::decode(&mut &encoded[..]).unwrap();
+ let standard = ::mul_projective(&base, &bigint);
+ assert_eq!(standard, result.0);
+ }
}
impl_benchmark_test_suite!(
diff --git a/pallets/gear-builtin/src/bls12_381.rs b/pallets/gear-builtin/src/bls12_381.rs
new file mode 100644
index 00000000000..a89ea561a5e
--- /dev/null
+++ b/pallets/gear-builtin/src/bls12_381.rs
@@ -0,0 +1,372 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+use super::*;
+use ark_scale::HOST_CALL;
+use ark_serialize::{CanonicalDeserialize, Compress, Validate};
+use core::marker::PhantomData;
+use gbuiltin_bls381::*;
+use parity_scale_codec::{Compact, Input};
+use sp_crypto_ec_utils::bls12_381;
+
+const IS_COMPRESSED: Compress = ark_scale::is_compressed(HOST_CALL);
+const IS_VALIDATED: Validate = ark_scale::is_validated(HOST_CALL);
+
+pub struct Actor(PhantomData);
+
+impl BuiltinActor for Actor {
+ const ID: u64 = 1;
+
+ type Error = BuiltinActorError;
+
+ fn handle(dispatch: &StoredDispatch, gas_limit: u64) -> (Result, u64) {
+ let message = dispatch.message();
+ let payload = message.payload_bytes();
+ let (result, gas_spent) = match payload.first().copied() {
+ Some(REQUEST_MULTI_MILLER_LOOP) => multi_miller_loop::(&payload[1..], gas_limit),
+ Some(REQUEST_FINAL_EXPONENTIATION) => {
+ final_exponentiation::(&payload[1..], gas_limit)
+ }
+ Some(REQUEST_MULTI_SCALAR_MULTIPLICATION_G1) => msm_g1::(&payload[1..], gas_limit),
+ Some(REQUEST_MULTI_SCALAR_MULTIPLICATION_G2) => msm_g2::(&payload[1..], gas_limit),
+ Some(REQUEST_PROJECTIVE_MULTIPLICATION_G1) => {
+ projective_multiplication_g1::(&payload[1..], gas_limit)
+ }
+ Some(REQUEST_PROJECTIVE_MULTIPLICATION_G2) => {
+ projective_multiplication_g2::(&payload[1..], gas_limit)
+ }
+ _ => (Err(BuiltinActorError::DecodingError), 0),
+ };
+
+ (
+ result.map(|response| {
+ response
+ .encode()
+ .try_into()
+ .unwrap_or_else(|_| unreachable!("Response message is too large"))
+ }),
+ gas_spent,
+ )
+ }
+}
+
+fn decode_vec(
+ gas_limit: u64,
+ mut gas_spent: u64,
+ input: &mut I,
+) -> (u64, Option, BuiltinActorError>>) {
+ let Ok(len) = Compact::::decode(input).map(u32::from) else {
+ log::debug!(
+ target: LOG_TARGET,
+ "Failed to scale-decode vector length"
+ );
+ return (gas_spent, Some(Err(BuiltinActorError::DecodingError)));
+ };
+
+ let to_spend = ::WeightInfo::decode_bytes(len).ref_time();
+ if gas_limit < gas_spent + to_spend {
+ return (gas_spent, None);
+ }
+
+ gas_spent += to_spend;
+
+ let mut items = vec![0u8; len as usize];
+ let bytes_slice = items.as_mut_slice();
+ let result = input.read(bytes_slice).map(|_| items).map_err(|_| {
+ log::debug!(
+ target: LOG_TARGET,
+ "Failed to scale-decode vector data",
+ );
+
+ BuiltinActorError::DecodingError
+ });
+
+ (gas_spent, Some(result))
+}
+
+fn multi_miller_loop(
+ mut payload: &[u8],
+ gas_limit: u64,
+) -> (Result, u64) {
+ // TODO: consider to refactor #3841
+ let (gas_spent, result) = decode_vec::(gas_limit, 0, &mut payload);
+ let a = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ let (mut gas_spent, result) = decode_vec::(gas_limit, gas_spent, &mut payload);
+ let b = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ // decode the count of items
+
+ let mut slice = a.as_slice();
+ let mut reader = ark_scale::rw::InputAsRead(&mut slice);
+ let Ok(count) = u64::deserialize_with_mode(&mut reader, IS_COMPRESSED, IS_VALIDATED) else {
+ log::debug!(
+ target: LOG_TARGET,
+ "Failed to decode item count in a",
+ );
+
+ return (Err(BuiltinActorError::DecodingError), gas_spent);
+ };
+
+ let mut slice = b.as_slice();
+ let mut reader = ark_scale::rw::InputAsRead(&mut slice);
+ match u64::deserialize_with_mode(&mut reader, IS_COMPRESSED, IS_VALIDATED) {
+ Ok(count_b) if count_b != count => {
+ return (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ "Multi Miller loop: non equal item count",
+ ))),
+ gas_spent,
+ )
+ }
+ Err(_) => return (Err(BuiltinActorError::DecodingError), gas_spent),
+ Ok(_) => (),
+ }
+
+ let to_spend = ::WeightInfo::bls12_381_multi_miller_loop(count as u32).ref_time();
+ if gas_limit < gas_spent + to_spend {
+ return (Err(BuiltinActorError::InsufficientGas), gas_spent);
+ }
+
+ gas_spent += to_spend;
+
+ match bls12_381::host_calls::bls12_381_multi_miller_loop(a, b) {
+ Ok(result) => (Ok(Response::MultiMillerLoop(result)), gas_spent),
+ Err(_) => (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ "Multi Miller loop: computation error",
+ ))),
+ gas_spent,
+ ),
+ }
+}
+
+fn final_exponentiation(
+ mut payload: &[u8],
+ gas_limit: u64,
+) -> (Result, u64) {
+ let (mut gas_spent, result) = decode_vec::(gas_limit, 0, &mut payload);
+ let f = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ let to_spend = ::WeightInfo::bls12_381_final_exponentiation().ref_time();
+ if gas_limit < gas_spent + to_spend {
+ return (Err(BuiltinActorError::InsufficientGas), gas_spent);
+ }
+
+ gas_spent += to_spend;
+
+ match bls12_381::host_calls::bls12_381_final_exponentiation(f) {
+ Ok(result) => (Ok(Response::FinalExponentiation(result)), gas_spent),
+ Err(_) => (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ "Final exponentiation: computation error",
+ ))),
+ gas_spent,
+ ),
+ }
+}
+
+fn msm(
+ mut payload: &[u8],
+ gas_limit: u64,
+ gas_to_spend: impl FnOnce(u32) -> u64,
+ call: impl FnOnce(Vec, Vec) -> Result,
+) -> (Result, u64) {
+ let (gas_spent, result) = decode_vec::(gas_limit, 0, &mut payload);
+ let bases = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ let (mut gas_spent, result) = decode_vec::(gas_limit, gas_spent, &mut payload);
+ let scalars = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ // decode the count of items
+
+ let mut slice = bases.as_slice();
+ let mut reader = ark_scale::rw::InputAsRead(&mut slice);
+ let Ok(count) = u64::deserialize_with_mode(&mut reader, IS_COMPRESSED, IS_VALIDATED) else {
+ log::debug!(
+ target: LOG_TARGET,
+ "Failed to decode item count in bases",
+ );
+
+ return (Err(BuiltinActorError::DecodingError), gas_spent);
+ };
+
+ let mut slice = scalars.as_slice();
+ let mut reader = ark_scale::rw::InputAsRead(&mut slice);
+ match u64::deserialize_with_mode(&mut reader, IS_COMPRESSED, IS_VALIDATED) {
+ Ok(count_b) if count_b != count => {
+ return (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ "Multi scalar multiplication: non equal item count",
+ ))),
+ gas_spent,
+ )
+ }
+ Err(_) => {
+ log::debug!(
+ target: LOG_TARGET,
+ "Failed to decode item count in scalars",
+ );
+
+ return (Err(BuiltinActorError::DecodingError), gas_spent);
+ }
+ Ok(_) => (),
+ }
+
+ let to_spend = gas_to_spend(count as u32);
+ if gas_limit < gas_spent + to_spend {
+ return (Err(BuiltinActorError::InsufficientGas), gas_spent);
+ }
+
+ gas_spent += to_spend;
+
+ match call(bases, scalars) {
+ Ok(result) => (Ok(result), gas_spent),
+ Err(_) => (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ "Multi scalar multiplication: computation error",
+ ))),
+ gas_spent,
+ ),
+ }
+}
+
+fn msm_g1(payload: &[u8], gas_limit: u64) -> (Result, u64) {
+ msm::(
+ payload,
+ gas_limit,
+ |count| ::WeightInfo::bls12_381_msm_g1(count).ref_time(),
+ |bases, scalars| {
+ bls12_381::host_calls::bls12_381_msm_g1(bases, scalars)
+ .map(Response::MultiScalarMultiplicationG1)
+ },
+ )
+}
+
+fn msm_g2(payload: &[u8], gas_limit: u64) -> (Result, u64) {
+ msm::(
+ payload,
+ gas_limit,
+ |count| ::WeightInfo::bls12_381_msm_g2(count).ref_time(),
+ |bases, scalars| {
+ bls12_381::host_calls::bls12_381_msm_g2(bases, scalars)
+ .map(Response::MultiScalarMultiplicationG2)
+ },
+ )
+}
+
+fn projective_multiplication(
+ mut payload: &[u8],
+ gas_limit: u64,
+ gas_to_spend: impl FnOnce(u32) -> u64,
+ call: impl FnOnce(Vec, Vec) -> Result,
+) -> (Result, u64) {
+ let (gas_spent, result) = decode_vec::(gas_limit, 0, &mut payload);
+ let base = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ let (mut gas_spent, result) = decode_vec::(gas_limit, gas_spent, &mut payload);
+ let scalar = match result {
+ Some(Ok(array)) => array,
+ Some(Err(e)) => return (Err(e), gas_spent),
+ None => return (Err(BuiltinActorError::InsufficientGas), gas_spent),
+ };
+
+ // decode the count of items
+
+ let mut slice = scalar.as_slice();
+ let mut reader = ark_scale::rw::InputAsRead(&mut slice);
+ let Ok(count) = u64::deserialize_with_mode(&mut reader, IS_COMPRESSED, IS_VALIDATED) else {
+ log::debug!(
+ target: LOG_TARGET,
+ "Failed to decode item count in scalar",
+ );
+
+ return (Err(BuiltinActorError::DecodingError), gas_spent);
+ };
+
+ let to_spend = gas_to_spend(count as u32);
+ if gas_limit < gas_spent + to_spend {
+ return (Err(BuiltinActorError::InsufficientGas), gas_spent);
+ }
+
+ gas_spent += to_spend;
+
+ match call(base, scalar) {
+ Ok(result) => (Ok(result), gas_spent),
+ Err(_) => (
+ Err(BuiltinActorError::Custom(LimitedStr::from_small_str(
+ "Projective multiplication: computation error",
+ ))),
+ gas_spent,
+ ),
+ }
+}
+
+fn projective_multiplication_g1(
+ payload: &[u8],
+ gas_limit: u64,
+) -> (Result, u64) {
+ projective_multiplication::(
+ payload,
+ gas_limit,
+ |count| ::WeightInfo::bls12_381_mul_projective_g1(count).ref_time(),
+ |base, scalar| {
+ bls12_381::host_calls::bls12_381_mul_projective_g1(base, scalar)
+ .map(Response::ProjectiveMultiplicationG1)
+ },
+ )
+}
+
+fn projective_multiplication_g2(
+ payload: &[u8],
+ gas_limit: u64,
+) -> (Result, u64) {
+ projective_multiplication::(
+ payload,
+ gas_limit,
+ |count| ::WeightInfo::bls12_381_mul_projective_g2(count).ref_time(),
+ |base, scalar| {
+ bls12_381::host_calls::bls12_381_mul_projective_g2(base, scalar)
+ .map(Response::ProjectiveMultiplicationG2)
+ },
+ )
+}
diff --git a/pallets/gear-builtin/src/lib.rs b/pallets/gear-builtin/src/lib.rs
index 211242217eb..b575d9d9392 100644
--- a/pallets/gear-builtin/src/lib.rs
+++ b/pallets/gear-builtin/src/lib.rs
@@ -34,6 +34,7 @@ extern crate alloc;
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
+pub mod bls12_381;
pub mod weights;
#[cfg(test)]
diff --git a/pallets/gear-builtin/src/mock.rs b/pallets/gear-builtin/src/mock.rs
index cf94c36d281..b2825123748 100644
--- a/pallets/gear-builtin/src/mock.rs
+++ b/pallets/gear-builtin/src/mock.rs
@@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use crate::{self as pallet_gear_builtin, BuiltinActor, BuiltinActorError};
+use crate::{self as pallet_gear_builtin, bls12_381, BuiltinActor, BuiltinActorError};
use common::{GasProvider, GasTree};
use core::cell::RefCell;
use frame_support::{
@@ -211,7 +211,12 @@ impl BuiltinActor for HonestBuiltinActor {
}
impl pallet_gear_builtin::Config for Test {
- type Builtins = (SuccessBuiltinActor, ErrorBuiltinActor, HonestBuiltinActor);
+ type Builtins = (
+ SuccessBuiltinActor,
+ ErrorBuiltinActor,
+ HonestBuiltinActor,
+ bls12_381::Actor,
+ );
type WeightInfo = ();
}
diff --git a/pallets/gear-builtin/src/tests/bls381.rs b/pallets/gear-builtin/src/tests/bls381.rs
new file mode 100644
index 00000000000..dac7bfa369b
--- /dev/null
+++ b/pallets/gear-builtin/src/tests/bls381.rs
@@ -0,0 +1,700 @@
+// This file is part of Gear.
+
+// Copyright (C) 2024 Gear Technologies Inc.
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+// This program 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.
+
+// This program 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 this program. If not, see .
+
+use crate::mock::*;
+use ark_bls12_381::{Bls12_381, G1Affine, G1Projective as G1, G2Affine, G2Projective as G2};
+use ark_ec::{
+ pairing::Pairing,
+ short_weierstrass::{Projective as SWProjective, SWCurveConfig},
+ Group, ScalarMul, VariableBaseMSM,
+};
+use ark_ff::biginteger::BigInt;
+use ark_scale::hazmat::ArkScaleProjective;
+use ark_std::{ops::Mul, UniformRand};
+use common::Origin;
+use frame_support::assert_ok;
+use gbuiltin_bls381::*;
+use gear_core::ids::ProgramId;
+use gear_core_errors::{ErrorReplyReason, ReplyCode, SimpleExecutionError};
+use pallet_gear::GasInfo;
+use parity_scale_codec::{Decode, Encode};
+use primitive_types::H256;
+
+type ArkScale = ark_scale::ArkScale;
+type ScalarField = ::ScalarField;
+
+const ACTOR_ID: [u8; 32] =
+ hex_literal::hex!("6b6e292c382945e80bf51af2ba7fe9f458dcff81ae6075c46f9095e1bbecdc37");
+
+pub(crate) fn init_logger() {
+ let _ = env_logger::Builder::from_default_env()
+ .format_module_path(false)
+ .format_level(true)
+ .try_init();
+}
+
+fn get_gas_info(builtin_id: ProgramId, payload: Vec) -> GasInfo {
+ start_transaction();
+ let res = Gear::calculate_gas_info(
+ SIGNER.into_origin(),
+ pallet_gear::manager::HandleKind::Handle(builtin_id),
+ payload,
+ 0,
+ true,
+ None,
+ None,
+ )
+ .expect("calculate_gas_info failed");
+ rollback_transaction();
+
+ assert_ne!(res.min_limit, 0);
+ assert_ne!(res.burned, 0);
+ // < 90% * block_gas_limit
+ assert!(res.burned < BlockGasLimit::get() / 10 * 9);
+
+ res
+}
+
+#[test]
+fn decoding_error() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let builtin_actor_id: ProgramId = H256::from(ACTOR_ID).cast();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ vec![255u8; 10],
+ 1_000_000_000,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::UserspacePanic,
+ )))
+ }
+ _ => false,
+ }));
+ });
+}
+
+#[test]
+fn multi_miller_loop() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let mut rng = ark_std::test_rng();
+
+ let message: G1Affine = G1::rand(&mut rng).into();
+ let priv_key: ScalarField = UniformRand::rand(&mut rng);
+ let generator: G2 = G2::generator();
+ let pub_key: G2Affine = generator.mul(priv_key).into();
+
+ let a: ArkScale::G1Affine>> = vec![message].into();
+ let b: ArkScale::G2Affine>> = vec![].into();
+ let payload = Request::MultiMillerLoop { a: a.encode(), b: b.encode(), }.encode();
+
+ // Case of the incorrect arguments
+ let builtin_id: ProgramId = H256::from(ACTOR_ID).cast();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_id,
+ payload.clone(),
+ 10_000_000_000,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::UserspacePanic,
+ )))
+ }
+ _ => false,
+ }));
+
+ let result = ::multi_miller_loop(vec![message], vec![pub_key]);
+
+ let a: ArkScale::G1Affine>> = vec![message].into();
+ let b: ArkScale::G2Affine>> = vec![pub_key].into();
+ let payload = Request::MultiMillerLoop { a: a.encode(), b: b.encode(), }.encode();
+
+ let builtin_id: ProgramId = H256::from(ACTOR_ID).cast();
+ let gas_info = get_gas_info(builtin_id, payload.clone());
+
+ // Check the case of insufficient gas
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_id,
+ payload.clone(),
+ gas_info.min_limit / 2,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::RanOutOfGas,
+ )))
+ }
+ _ => false,
+ }));
+
+ // Check the computations are correct
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_id,
+ payload,
+ gas_info.min_limit,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ let response = match System::events().into_iter().find_map(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ assert_eq!(message.destination(), ProgramId::from(SIGNER));
+ assert!(matches!(message.details(), Some(details) if matches!(details.to_reply_code(), ReplyCode::Success(..))));
+
+ Some(message.payload_bytes().to_vec())
+ }
+
+ _ => None,
+ }) {
+ Some(response) => response,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = match Response::decode(&mut response.as_slice()) {
+ Ok(Response::MultiMillerLoop(builtin_result)) => builtin_result,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = ArkScale::<::TargetField>::decode(&mut builtin_result.as_slice()).unwrap();
+ assert_eq!(result.0, builtin_result.0);
+ });
+}
+
+#[test]
+fn final_exponentiation() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let mut rng = ark_std::test_rng();
+
+ // message
+ let message: G1Affine = G1::rand(&mut rng).into();
+ let priv_key: ScalarField = UniformRand::rand(&mut rng);
+ let generator: G2 = G2::generator();
+ let pub_key: G2Affine = generator.mul(priv_key).into();
+
+ let loop_result = ::multi_miller_loop(vec![message], vec![pub_key]);
+ let result = ::final_exponentiation(loop_result);
+
+ let f: ArkScale<::TargetField> = loop_result.0.into();
+ let payload = Request::FinalExponentiation { f: f.encode() }.encode();
+
+ let builtin_actor_id: ProgramId = H256::from(ACTOR_ID).cast();
+ let gas_info = get_gas_info(builtin_actor_id, payload.clone());
+
+ // check case of insufficient gas
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload.clone(),
+ gas_info.min_limit / 2,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::RanOutOfGas,
+ )))
+ }
+ _ => false,
+ }));
+
+ // check the computations are correct
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload,
+ gas_info.min_limit,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ let response = match System::events().into_iter().find_map(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ assert_eq!(message.destination(), ProgramId::from(SIGNER));
+ assert!(matches!(message.details(), Some(details) if matches!(details.to_reply_code(), ReplyCode::Success(..))));
+
+ Some(message.payload_bytes().to_vec())
+ }
+
+ _ => None,
+ }) {
+ Some(response) => response,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = match Response::decode(&mut response.as_slice()) {
+ Ok(Response::FinalExponentiation(builtin_result)) => builtin_result,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = ArkScale::<::TargetField>::decode(&mut builtin_result.as_slice()).unwrap();
+ assert!(matches!(result, Some(r) if r.0 == builtin_result.0));
+ });
+}
+
+#[test]
+fn msm_g1() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let mut rng = ark_std::test_rng();
+
+ let count = 5usize;
+
+ let scalars = (0..count)
+ .map(|_| ::ScalarField::rand(&mut rng))
+ .collect::>();
+
+ let bases = (0..count).map(|_| G1::rand(&mut rng)).collect::>();
+ let bases = G1::batch_convert_to_mul_base(&bases);
+
+ let ark_scalars: ArkScale::ScalarField>> = scalars[1..].to_vec().into();
+ let ark_bases: ArkScale> = bases.clone().into();
+
+ let payload = Request::MultiScalarMultiplicationG1 { bases: ark_bases.encode(), scalars: ark_scalars.encode() }.encode();
+
+ // Case of the incorrect arguments
+ let builtin_id: ProgramId = H256::from(ACTOR_ID).cast();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_id,
+ payload.clone(),
+ 10_000_000_000,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::UserspacePanic,
+ )))
+ }
+ _ => false,
+ }));
+
+ let result = as VariableBaseMSM>::msm(&bases, &scalars);
+
+ let ark_scalars: ArkScale::ScalarField>> = scalars.into();
+ let ark_bases: ArkScale> = bases.into();
+
+ let payload = Request::MultiScalarMultiplicationG1 { bases: ark_bases.encode(), scalars: ark_scalars.encode() }.encode();
+
+ let builtin_actor_id: ProgramId = H256::from(ACTOR_ID).cast();
+ let gas_info = get_gas_info(builtin_actor_id, payload.clone());
+
+ // Check the case of insufficient gas
+ System::reset_events();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload.clone(),
+ gas_info.min_limit / 2,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::RanOutOfGas,
+ )))
+ }
+ _ => false,
+ }));
+
+ // Check the computations are correct
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload,
+ gas_info.min_limit,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ let response = match System::events().into_iter().find_map(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ assert_eq!(message.destination(), ProgramId::from(SIGNER));
+ assert!(matches!(message.details(), Some(details) if matches!(details.to_reply_code(), ReplyCode::Success(..))));
+
+ Some(message.payload_bytes().to_vec())
+ }
+
+ _ => None,
+ }) {
+ Some(response) => response,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = match Response::decode(&mut response.as_slice()) {
+ Ok(Response::MultiScalarMultiplicationG1(builtin_result)) => builtin_result,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = ArkScaleProjective::::decode(&mut builtin_result.as_slice()).unwrap();
+ assert!(matches!(result, Ok(r) if r == builtin_result.0));
+ });
+}
+
+#[test]
+fn msm_g2() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let mut rng = ark_std::test_rng();
+
+ let count = 5usize;
+
+ let scalars = (0..count)
+ .map(|_| ::ScalarField::rand(&mut rng))
+ .collect::>();
+
+ let bases = (0..count).map(|_| G2::rand(&mut rng)).collect::>();
+ let bases = G2::batch_convert_to_mul_base(&bases);
+
+ let ark_scalars: ArkScale::ScalarField>> = scalars[1..].to_vec().into();
+ let ark_bases: ArkScale> = bases.clone().into();
+
+ let payload = Request::MultiScalarMultiplicationG1 { bases: ark_bases.encode(), scalars: ark_scalars.encode() }.encode();
+
+ // Case of the incorrect arguments
+ let builtin_id: ProgramId = H256::from(ACTOR_ID).cast();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_id,
+ payload.clone(),
+ 10_000_000_000,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::UserspacePanic,
+ )))
+ }
+ _ => false,
+ }));
+
+ let result = as VariableBaseMSM>::msm(&bases, &scalars);
+
+ let ark_scalars: ArkScale::ScalarField>> = scalars.into();
+ let ark_bases: ArkScale> = bases.into();
+
+ let payload = Request::MultiScalarMultiplicationG2 { bases: ark_bases.encode(), scalars: ark_scalars.encode() }.encode();
+
+ let builtin_actor_id: ProgramId = H256::from(ACTOR_ID).cast();
+ let gas_info = get_gas_info(builtin_actor_id, payload.clone());
+
+ // Check the case of insufficient gas
+ System::reset_events();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload.clone(),
+ gas_info.min_limit / 2,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::RanOutOfGas,
+ )))
+ }
+ _ => false,
+ }));
+
+ // Check the computations are correct
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload,
+ gas_info.min_limit,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ let response = match System::events().into_iter().find_map(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ assert_eq!(message.destination(), ProgramId::from(SIGNER));
+ assert!(matches!(message.details(), Some(details) if matches!(details.to_reply_code(), ReplyCode::Success(..))));
+
+ Some(message.payload_bytes().to_vec())
+ }
+
+ _ => None,
+ }) {
+ Some(response) => response,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = match Response::decode(&mut response.as_slice()) {
+ Ok(Response::MultiScalarMultiplicationG2(builtin_result)) => builtin_result,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = ArkScaleProjective::::decode(&mut builtin_result.as_slice()).unwrap();
+ assert!(matches!(result, Ok(r) if r == builtin_result.0));
+ });
+}
+
+#[test]
+fn mul_projective_g1() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let mut rng = ark_std::test_rng();
+
+ let bigint = BigInt::<3>::rand(&mut rng);
+ let bigint = bigint.0.to_vec();
+ let base = G1::rand(&mut rng);
+
+ let result = ::mul_projective(&base, &bigint);
+
+ let ark_bigint: ArkScale> = bigint.into();
+ let ark_base: ArkScaleProjective = base.into();
+ let payload = Request::ProjectiveMultiplicationG1 { base: ark_base.encode(), scalar: ark_bigint.encode() }.encode();
+ let builtin_actor_id: ProgramId = H256::from(ACTOR_ID).cast();
+ let gas_info = get_gas_info(builtin_actor_id, payload.clone());
+
+ // Check the case of insufficient gas
+ System::reset_events();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload.clone(),
+ gas_info.min_limit / 2,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::RanOutOfGas,
+ )))
+ }
+ _ => false,
+ }));
+
+ // Check the computations are correct
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload,
+ gas_info.min_limit,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ let response = match System::events().into_iter().find_map(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ assert_eq!(message.destination(), ProgramId::from(SIGNER));
+ assert!(matches!(message.details(), Some(details) if matches!(details.to_reply_code(), ReplyCode::Success(..))));
+
+ Some(message.payload_bytes().to_vec())
+ }
+
+ _ => None,
+ }) {
+ Some(response) => response,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = match Response::decode(&mut response.as_slice()) {
+ Ok(Response::ProjectiveMultiplicationG1(builtin_result)) => builtin_result,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = ArkScaleProjective::::decode(&mut builtin_result.as_slice()).unwrap();
+ assert_eq!(result, builtin_result.0);
+ });
+}
+
+#[test]
+fn mul_projective_g2() {
+ init_logger();
+
+ new_test_ext().execute_with(|| {
+ let mut rng = ark_std::test_rng();
+
+ let bigint = BigInt::<3>::rand(&mut rng);
+ let bigint = bigint.0.to_vec();
+ let base = G2::rand(&mut rng);
+
+ let result = ::mul_projective(&base, &bigint);
+
+ let ark_bigint: ArkScale> = bigint.into();
+ let ark_base: ArkScaleProjective = base.into();
+ let payload = Request::ProjectiveMultiplicationG2 { base: ark_base.encode(), scalar: ark_bigint.encode() }.encode();
+ let builtin_actor_id: ProgramId = H256::from(ACTOR_ID).cast();
+ let gas_info = get_gas_info(builtin_actor_id, payload.clone());
+
+ // Check the case of insufficient gas
+ System::reset_events();
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload.clone(),
+ gas_info.min_limit / 2,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ // An error reply should have been sent.
+ assert!(System::events().into_iter().any(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ message.destination() == ProgramId::from(SIGNER)
+ && matches!(message.details(), Some(details) if details.to_reply_code()
+ == ReplyCode::Error(ErrorReplyReason::Execution(
+ SimpleExecutionError::RanOutOfGas,
+ )))
+ }
+ _ => false,
+ }));
+
+ // Check the computations are correct
+ System::reset_events();
+
+ assert_ok!(Gear::send_message(
+ RuntimeOrigin::signed(SIGNER),
+ builtin_actor_id,
+ payload,
+ gas_info.min_limit,
+ 0,
+ false,
+ ));
+
+ run_to_next_block();
+
+ let response = match System::events().into_iter().find_map(|e| match e.event {
+ RuntimeEvent::Gear(pallet_gear::Event::::UserMessageSent { message, .. }) => {
+ assert_eq!(message.destination(), ProgramId::from(SIGNER));
+ assert!(matches!(message.details(), Some(details) if matches!(details.to_reply_code(), ReplyCode::Success(..))));
+
+ Some(message.payload_bytes().to_vec())
+ }
+
+ _ => None,
+ }) {
+ Some(response) => response,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = match Response::decode(&mut response.as_slice()) {
+ Ok(Response::ProjectiveMultiplicationG2(builtin_result)) => builtin_result,
+ _ => unreachable!(),
+ };
+
+ let builtin_result = ArkScaleProjective::::decode(&mut builtin_result.as_slice()).unwrap();
+ assert_eq!(result, builtin_result.0);
+ });
+}
diff --git a/pallets/gear-builtin/src/tests/mod.rs b/pallets/gear-builtin/src/tests/mod.rs
index 3166e9ec895..b90c278c4c5 100644
--- a/pallets/gear-builtin/src/tests/mod.rs
+++ b/pallets/gear-builtin/src/tests/mod.rs
@@ -20,3 +20,4 @@
pub mod bad_builtin_ids;
pub mod basic;
+pub mod bls381;
diff --git a/pallets/gear-builtin/src/weights.rs b/pallets/gear-builtin/src/weights.rs
index eee56c79343..feffe25a9a8 100644
--- a/pallets/gear-builtin/src/weights.rs
+++ b/pallets/gear-builtin/src/weights.rs
@@ -51,6 +51,13 @@ use sp_std::marker::PhantomData;
pub trait WeightInfo {
fn calculate_id() -> Weight;
fn create_dispatcher() -> Weight;
+ fn decode_bytes(a: u32, ) -> Weight;
+ fn bls12_381_multi_miller_loop(c: u32, ) -> Weight;
+ fn bls12_381_final_exponentiation() -> Weight;
+ fn bls12_381_msm_g1(c: u32, ) -> Weight;
+ fn bls12_381_msm_g2(c: u32, ) -> Weight;
+ fn bls12_381_mul_projective_g1(c: u32, ) -> Weight;
+ fn bls12_381_mul_projective_g2(c: u32, ) -> Weight;
}
/// Weights for `pallet_gear_builtin` using a Gear node and recommended hardware.
@@ -72,6 +79,73 @@ impl WeightInfo for SubstrateWeight {
Weight::from_parts(7_000_000, 0)
.saturating_add(Weight::from_parts(0, 0))
}
+ /// The range of component `a` is `[0, 33554332]`.
+ fn decode_bytes(a: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 0_000 picoseconds.
+ Weight::from_parts(0, 0)
+ // Standard Error: 0
+ .saturating_add(Weight::from_parts(160, 0).saturating_mul(a.into()))
+ }
+ /// The range of component `c` is `[0, 100]`.
+ fn bls12_381_multi_miller_loop(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 7_000_000 picoseconds.
+ Weight::from_parts(273_990_134, 0)
+ // Standard Error: 723_591
+ .saturating_add(Weight::from_parts(149_551_274, 0).saturating_mul(c.into()))
+ }
+ fn bls12_381_final_exponentiation() -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 539_000_000 picoseconds.
+ Weight::from_parts(554_000_000, 0)
+ }
+ /// The range of component `c` is `[0, 1000]`.
+ fn bls12_381_msm_g1(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 25_000_000 picoseconds.
+ Weight::from_parts(522_081_240, 0)
+ // Standard Error: 103_908
+ .saturating_add(Weight::from_parts(4_129_174, 0).saturating_mul(c.into()))
+ }
+ /// The range of component `c` is `[0, 1000]`.
+ fn bls12_381_msm_g2(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 52_000_000 picoseconds.
+ Weight::from_parts(521_472_222, 0)
+ // Standard Error: 451_388
+ .saturating_add(Weight::from_parts(12_974_500, 0).saturating_mul(c.into()))
+ }
+ /// The range of component `c` is `[1, 100]`.
+ fn bls12_381_mul_projective_g1(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 35_000_000 picoseconds.
+ Weight::from_parts(36_000_000, 0)
+ // Standard Error: 92_373
+ .saturating_add(Weight::from_parts(38_727_595, 0).saturating_mul(c.into()))
+ }
+ /// The range of component `c` is `[1, 100]`.
+ fn bls12_381_mul_projective_g2(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 110_000_000 picoseconds.
+ Weight::from_parts(111_000_000, 0)
+ // Standard Error: 286_785
+ .saturating_add(Weight::from_parts(114_799_709, 0).saturating_mul(c.into()))
+ }
}
impl WeightInfo for () {
@@ -91,4 +165,71 @@ impl WeightInfo for () {
Weight::from_parts(8_000_000, 0)
.saturating_add(Weight::from_parts(0, 0))
}
+ /// The range of component `a` is `[0, 33554332]`.
+ fn decode_bytes(a: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 0_000 picoseconds.
+ Weight::from_parts(0, 0)
+ // Standard Error: 0
+ .saturating_add(Weight::from_parts(160, 0).saturating_mul(a.into()))
+ }
+ /// The range of component `c` is `[0, 100]`.
+ fn bls12_381_multi_miller_loop(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 7_000_000 picoseconds.
+ Weight::from_parts(273_990_134, 0)
+ // Standard Error: 723_591
+ .saturating_add(Weight::from_parts(149_551_274, 0).saturating_mul(c.into()))
+ }
+ fn bls12_381_final_exponentiation() -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 539_000_000 picoseconds.
+ Weight::from_parts(554_000_000, 0)
+ }
+ /// The range of component `c` is `[0, 1000]`.
+ fn bls12_381_msm_g1(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 25_000_000 picoseconds.
+ Weight::from_parts(522_081_240, 0)
+ // Standard Error: 103_908
+ .saturating_add(Weight::from_parts(4_129_174, 0).saturating_mul(c.into()))
+ }
+ /// The range of component `c` is `[0, 1000]`.
+ fn bls12_381_msm_g2(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 52_000_000 picoseconds.
+ Weight::from_parts(521_472_222, 0)
+ // Standard Error: 451_388
+ .saturating_add(Weight::from_parts(12_974_500, 0).saturating_mul(c.into()))
+ }
+ /// The range of component `c` is `[1, 100]`.
+ fn bls12_381_mul_projective_g1(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 35_000_000 picoseconds.
+ Weight::from_parts(36_000_000, 0)
+ // Standard Error: 92_373
+ .saturating_add(Weight::from_parts(38_727_595, 0).saturating_mul(c.into()))
+ }
+ /// The range of component `c` is `[1, 100]`.
+ fn bls12_381_mul_projective_g2(c: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 110_000_000 picoseconds.
+ Weight::from_parts(111_000_000, 0)
+ // Standard Error: 286_785
+ .saturating_add(Weight::from_parts(114_799_709, 0).saturating_mul(c.into()))
+ }
}
diff --git a/runtime/vara/src/lib.rs b/runtime/vara/src/lib.rs
index 703aa4cdea6..12bedaff9f6 100644
--- a/runtime/vara/src/lib.rs
+++ b/runtime/vara/src/lib.rs
@@ -1058,7 +1058,7 @@ impl pallet_gear_messenger::Config for Runtime {
/// Builtin actors arranged in a tuple.
#[cfg(not(feature = "runtime-benchmarks"))]
-pub type BuiltinActors = ();
+pub type BuiltinActors = (pallet_gear_builtin::bls12_381::Actor,);
#[cfg(feature = "runtime-benchmarks")]
pub type BuiltinActors = pallet_gear_builtin::benchmarking::BenchmarkingBuiltinActor;
diff --git a/utils/crates-io/src/handler.rs b/utils/crates-io/src/handler.rs
index 3a434923cec..08843f3cabc 100644
--- a/utils/crates-io/src/handler.rs
+++ b/utils/crates-io/src/handler.rs
@@ -277,6 +277,14 @@ mod substrate {
table.insert("version", GP_RUNTIME_INTERFACE_VERSION.into());
table.insert("package", "gp-runtime-interface".into());
}
+ // Depends on sp-wasm-interface.
+ //
+ // ref:
+ // - sp-runtime-interface-18.0.0
+ // - sp-runtime-interface-proc-macro-12.0.0
+ "sp-crypto-ec-utils" => {
+ table.insert("package", "gp-crypto-ec-utils".into());
+ }
_ => {}
}
diff --git a/utils/wasm-proc/src/main.rs b/utils/wasm-proc/src/main.rs
index b30391dfa84..92cc67724f1 100644
--- a/utils/wasm-proc/src/main.rs
+++ b/utils/wasm-proc/src/main.rs
@@ -24,7 +24,7 @@ use gear_wasm_builder::{
use parity_wasm::elements::External;
use std::{collections::HashSet, fs, path::PathBuf};
-const RT_ALLOWED_IMPORTS: [&str; 67] = [
+const RT_ALLOWED_IMPORTS: [&str; 73] = [
// From `Allocator` (substrate/primitives/io/src/lib.rs)
"ext_allocator_free_version_1",
"ext_allocator_malloc_version_1",
@@ -103,6 +103,13 @@ const RT_ALLOWED_IMPORTS: [&str; 67] = [
"ext_storage_start_transaction_version_1",
// From `Trie` (substrate/primitives/io/src/lib.rs)
"ext_trie_blake2_256_ordered_root_version_2",
+ // From `sp-crypto-ec-utils`
+ "ext_host_calls_bls12_381_final_exponentiation_version_1",
+ "ext_host_calls_bls12_381_msm_g1_version_1",
+ "ext_host_calls_bls12_381_msm_g2_version_1",
+ "ext_host_calls_bls12_381_mul_projective_g1_version_1",
+ "ext_host_calls_bls12_381_mul_projective_g2_version_1",
+ "ext_host_calls_bls12_381_multi_miller_loop_version_1",
];
#[derive(Debug, clap::Parser)]