diff --git a/Cargo.lock b/Cargo.lock index cb84028670600..0a869cfbf2f7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,7 @@ dependencies = [ "serde-name", "serde_bytes", "serde_json", + "sha2 0.10.6", "sha2 0.9.9", "sha3 0.9.1", "static_assertions", @@ -1257,6 +1258,7 @@ dependencies = [ "aptos-crypto", "aptos-gas", "aptos-gas-algebra-ext", + "aptos-move-stdlib", "aptos-sdk-builder", "aptos-state-view", "aptos-types", @@ -1308,6 +1310,7 @@ dependencies = [ "serde_bytes", "serde_json", "serde_yaml 0.8.26", + "sha2 0.10.6", "sha2 0.9.9", "sha3 0.9.1", "siphasher", diff --git a/Cargo.toml b/Cargo.toml index 0b5e83d52b944..04a9d692da7a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -524,6 +524,7 @@ rstest = "0.15.0" rusty-fork = "0.3.0" sha-1 = "0.10.0" sha2 = "0.9.3" +sha2_0_10_6 = { package = "sha2", version = "0.10.6" } sha3 = "0.9.1" siphasher = "0.3.10" serde = { version = "1.0.137", features = ["derive", "rc"] } diff --git a/aptos-move/aptos-gas/src/aptos_framework.rs b/aptos-move/aptos-gas/src/aptos_framework.rs index fb4614286394b..f5ab7de5c7626 100644 --- a/aptos-move/aptos-gas/src/aptos_framework.rs +++ b/aptos-move/aptos-gas/src/aptos_framework.rs @@ -8,6 +8,71 @@ crate::natives::define_gas_parameters_for_natives!(GasParameters, "aptos_framewo [.account.create_address.base, "account.create_address.base", 300 * MUL], [.account.create_signer.base, "account.create_signer.base", 300 * MUL], + // Algebra gas parameters begin. + // Generated at time 1680606720.0709136 by `scripts/algebra-gas/update_algebra_gas_params.py` with gas_per_ns=10.23. + [.algebra.ark_bls12_381_fq12_add, { 8.. => "algebra.ark_bls12_381_fq12_add" }, 1_819 * MUL], + [.algebra.ark_bls12_381_fq12_clone, { 8.. => "algebra.ark_bls12_381_fq12_clone" }, 211 * MUL], + [.algebra.ark_bls12_381_fq12_deser, { 8.. => "algebra.ark_bls12_381_fq12_deser" }, 11_180 * MUL], + [.algebra.ark_bls12_381_fq12_div, { 8.. => "algebra.ark_bls12_381_fq12_div" }, 250_813 * MUL], + [.algebra.ark_bls12_381_fq12_eq, { 8.. => "algebra.ark_bls12_381_fq12_eq" }, 726 * MUL], + [.algebra.ark_bls12_381_fq12_from_u64, { 8.. => "algebra.ark_bls12_381_fq12_from_u64" }, 901 * MUL], + [.algebra.ark_bls12_381_fq12_inv, { 8.. => "algebra.ark_bls12_381_fq12_inv" }, 200_523 * MUL], + [.algebra.ark_bls12_381_fq12_mul, { 8.. => "algebra.ark_bls12_381_fq12_mul" }, 49_886 * MUL], + [.algebra.ark_bls12_381_fq12_neg, { 8.. => "algebra.ark_bls12_381_fq12_neg" }, 1_181 * MUL], + [.algebra.ark_bls12_381_fq12_one, { 8.. => "algebra.ark_bls12_381_fq12_one" }, 11 * MUL], + [.algebra.ark_bls12_381_fq12_pow_u256, { 8.. => "algebra.ark_bls12_381_fq12_pow_u256" }, 14_664_207 * MUL], + [.algebra.ark_bls12_381_fq12_serialize, { 8.. => "algebra.ark_bls12_381_fq12_serialize" }, 8_078 * MUL], + [.algebra.ark_bls12_381_fq12_square, { 8.. => "algebra.ark_bls12_381_fq12_square" }, 35_145 * MUL], + [.algebra.ark_bls12_381_fq12_sub, { 8.. => "algebra.ark_bls12_381_fq12_sub" }, 1_758 * MUL], + [.algebra.ark_bls12_381_fq12_zero, { 8.. => "algebra.ark_bls12_381_fq12_zero" }, 211 * MUL], + [.algebra.ark_bls12_381_fr_add, { 8.. => "algebra.ark_bls12_381_fr_add" }, 211 * MUL], + [.algebra.ark_bls12_381_fr_deser, { 8.. => "algebra.ark_bls12_381_fr_deser" }, 752 * MUL], + [.algebra.ark_bls12_381_fr_div, { 8.. => "algebra.ark_bls12_381_fr_div" }, 59_440 * MUL], + [.algebra.ark_bls12_381_fr_eq, { 8.. => "algebra.ark_bls12_381_fr_eq" }, 212 * MUL], + [.algebra.ark_bls12_381_fr_from_u64, { 8.. => "algebra.ark_bls12_381_fr_from_u64" }, 494 * MUL], + [.algebra.ark_bls12_381_fr_inv, { 8.. => "algebra.ark_bls12_381_fr_inv" }, 58_610 * MUL], + [.algebra.ark_bls12_381_fr_mul, { 8.. => "algebra.ark_bls12_381_fr_mul" }, 502 * MUL], + [.algebra.ark_bls12_381_fr_neg, { 8.. => "algebra.ark_bls12_381_fr_neg" }, 213 * MUL], + [.algebra.ark_bls12_381_fr_one, { 8.. => "algebra.ark_bls12_381_fr_one" }, 211 * MUL], + [.algebra.ark_bls12_381_fr_serialize, { 8.. => "algebra.ark_bls12_381_fr_serialize" }, 1_103 * MUL], + [.algebra.ark_bls12_381_fr_square, { 8.. => "algebra.ark_bls12_381_fr_square" }, 475 * MUL], + [.algebra.ark_bls12_381_fr_sub, { 8.. => "algebra.ark_bls12_381_fr_sub" }, 290 * MUL], + [.algebra.ark_bls12_381_fr_zero, { 8.. => "algebra.ark_bls12_381_fr_zero" }, 211 * MUL], + [.algebra.ark_bls12_381_g1_affine_deser_comp, { 8.. => "algebra.ark_bls12_381_g1_affine_deser_comp" }, 1_029_599 * MUL], + [.algebra.ark_bls12_381_g1_affine_deser_uncomp, { 8.. => "algebra.ark_bls12_381_g1_affine_deser_uncomp" }, 720_638 * MUL], + [.algebra.ark_bls12_381_g1_affine_serialize_comp, { 8.. => "algebra.ark_bls12_381_g1_affine_serialize_comp" }, 2_014 * MUL], + [.algebra.ark_bls12_381_g1_affine_serialize_uncomp, { 8.. => "algebra.ark_bls12_381_g1_affine_serialize_uncomp" }, 2_433 * MUL], + [.algebra.ark_bls12_381_g1_proj_add, { 8.. => "algebra.ark_bls12_381_g1_proj_add" }, 10_806 * MUL], + [.algebra.ark_bls12_381_g1_proj_double, { 8.. => "algebra.ark_bls12_381_g1_proj_double" }, 5_264 * MUL], + [.algebra.ark_bls12_381_g1_proj_eq, { 8.. => "algebra.ark_bls12_381_g1_proj_eq" }, 5_035 * MUL], + [.algebra.ark_bls12_381_g1_proj_generator, { 8.. => "algebra.ark_bls12_381_g1_proj_generator" }, 11 * MUL], + [.algebra.ark_bls12_381_g1_proj_infinity, { 8.. => "algebra.ark_bls12_381_g1_proj_infinity" }, 11 * MUL], + [.algebra.ark_bls12_381_g1_proj_neg, { 8.. => "algebra.ark_bls12_381_g1_proj_neg" }, 11 * MUL], + [.algebra.ark_bls12_381_g1_proj_scalar_mul, { 8.. => "algebra.ark_bls12_381_g1_proj_scalar_mul" }, 2_523_521 * MUL], + [.algebra.ark_bls12_381_g1_proj_sub, { 8.. => "algebra.ark_bls12_381_g1_proj_sub" }, 11_147 * MUL], + [.algebra.ark_bls12_381_g1_proj_to_affine, { 8.. => "algebra.ark_bls12_381_g1_proj_to_affine" }, 121_035 * MUL], + [.algebra.ark_bls12_381_g2_affine_deser_comp, { 8.. => "algebra.ark_bls12_381_g2_affine_deser_comp" }, 2_060_068 * MUL], + [.algebra.ark_bls12_381_g2_affine_deser_uncomp, { 8.. => "algebra.ark_bls12_381_g2_affine_deser_uncomp" }, 1_017_979 * MUL], + [.algebra.ark_bls12_381_g2_affine_serialize_comp, { 8.. => "algebra.ark_bls12_381_g2_affine_serialize_comp" }, 3_378 * MUL], + [.algebra.ark_bls12_381_g2_affine_serialize_uncomp, { 8.. => "algebra.ark_bls12_381_g2_affine_serialize_uncomp" }, 4_217 * MUL], + [.algebra.ark_bls12_381_g2_proj_add, { 8.. => "algebra.ark_bls12_381_g2_proj_add" }, 32_401 * MUL], + [.algebra.ark_bls12_381_g2_proj_double, { 8.. => "algebra.ark_bls12_381_g2_proj_double" }, 14_839 * MUL], + [.algebra.ark_bls12_381_g2_proj_eq, { 8.. => "algebra.ark_bls12_381_g2_proj_eq" }, 15_155 * MUL], + [.algebra.ark_bls12_381_g2_proj_generator, { 8.. => "algebra.ark_bls12_381_g2_proj_generator" }, 11 * MUL], + [.algebra.ark_bls12_381_g2_proj_infinity, { 8.. => "algebra.ark_bls12_381_g2_proj_infinity" }, 11 * MUL], + [.algebra.ark_bls12_381_g2_proj_neg, { 8.. => "algebra.ark_bls12_381_g2_proj_neg" }, 11 * MUL], + [.algebra.ark_bls12_381_g2_proj_scalar_mul, { 8.. => "algebra.ark_bls12_381_g2_proj_scalar_mul" }, 7_526_508 * MUL], + [.algebra.ark_bls12_381_g2_proj_sub, { 8.. => "algebra.ark_bls12_381_g2_proj_sub" }, 32_869 * MUL], + [.algebra.ark_bls12_381_g2_proj_to_affine, { 8.. => "algebra.ark_bls12_381_g2_proj_to_affine" }, 128_857 * MUL], + [.algebra.ark_bls12_381_multi_pairing_base, { 8.. => "algebra.ark_bls12_381_multi_pairing_base" }, 8_998_649 * MUL], + [.algebra.ark_bls12_381_multi_pairing_per_pair, { 8.. => "algebra.ark_bls12_381_multi_pairing_per_pair" }, 4_602_642 * MUL], + [.algebra.ark_bls12_381_pairing, { 8.. => "algebra.ark_bls12_381_pairing" }, 14_832_220 * MUL], + [.algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_base, { 8.. => "algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_base" }, 3_251_943 * MUL], + [.algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte, { 8.. => "algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte" }, 48 * MUL], + [.algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_base, { 8.. => "algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_base" }, 6_773_002 * MUL], + [.algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte, { 8.. => "algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte" }, 48 * MUL], + // Algebra gas parameters end. + [.bls12381.base, "bls12381.base", 150 * MUL], [.bls12381.per_pubkey_deserialize, "bls12381.per_pubkey_deserialize", 109_000 * MUL], diff --git a/aptos-move/aptos-gas/src/gas_meter.rs b/aptos-move/aptos-gas/src/gas_meter.rs index 84b789c59d55e..71c0a00ea4285 100644 --- a/aptos-move/aptos-gas/src/gas_meter.rs +++ b/aptos-move/aptos-gas/src/gas_meter.rs @@ -33,6 +33,8 @@ use move_vm_types::{ use std::collections::BTreeMap; // Change log: +// - V8 +// - Added BLS12-381 operations. // - V7 // - Native support for exists // - New formulae for storage fees based on fixed APT costs @@ -57,7 +59,7 @@ use std::collections::BTreeMap; // global operations. // - V1 // - TBA -pub const LATEST_GAS_FEATURE_VERSION: u64 = 7; +pub const LATEST_GAS_FEATURE_VERSION: u64 = 8; pub(crate) const EXECUTION_GAS_MULTIPLIER: u64 = 20; diff --git a/aptos-move/aptos-vm/src/natives.rs b/aptos-move/aptos-vm/src/natives.rs index c2e2f58b3c4d8..685314377035a 100644 --- a/aptos-move/aptos-vm/src/natives.rs +++ b/aptos-move/aptos-vm/src/natives.rs @@ -35,11 +35,12 @@ pub fn aptos_natives( timed_features: TimedFeatures, features: Arc, ) -> NativeFunctionTable { - aptos_move_stdlib::natives::all_natives(CORE_CODE_ADDRESS, gas_params.move_stdlib) + aptos_move_stdlib::natives::all_natives(CORE_CODE_ADDRESS, gas_params.move_stdlib.clone()) .into_iter() .filter(|(_, name, _, _)| name.as_str() != "vector") .chain(aptos_framework::natives::all_natives( CORE_CODE_ADDRESS, + gas_params.move_stdlib, gas_params.aptos_framework, timed_features, features, diff --git a/aptos-move/framework/Cargo.toml b/aptos-move/framework/Cargo.toml index 49905bef172c1..c9716d10df749 100644 --- a/aptos-move/framework/Cargo.toml +++ b/aptos-move/framework/Cargo.toml @@ -17,6 +17,7 @@ anyhow = { workspace = true } aptos-aggregator = { workspace = true } aptos-crypto = { workspace = true, features = ["fuzzing"] } aptos-gas-algebra-ext = { workspace = true } +aptos-move-stdlib = { workspace = true } aptos-sdk-builder = { workspace = true } aptos-state-view = { workspace = true } aptos-types = { workspace = true } @@ -65,6 +66,7 @@ serde_bytes = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } sha2 = { workspace = true } +sha2_0_10_6 = { workspace = true } sha3 = { workspace = true } siphasher = { workspace = true } smallvec = { workspace = true } diff --git a/aptos-move/framework/aptos-stdlib/doc/algebra.md b/aptos-move/framework/aptos-stdlib/doc/algebra.md deleted file mode 100644 index 0ea19cfbc516d..0000000000000 --- a/aptos-move/framework/aptos-stdlib/doc/algebra.md +++ /dev/null @@ -1,291 +0,0 @@ - - - -# Module `0x1::algebra` - -This module provides generic structs/functions for operations of algebraic structures (e.g. fields and groups), -which can be used to build generic cryptographic schemes atop. -See algebra_*.move for currently implemented algebraic structures. - -Currently supported operations include element serialization/deserialization and addition. - - -- [Struct `Element`](#0x1_algebra_Element) -- [Function `add`](#0x1_algebra_add) -- [Function `deserialize`](#0x1_algebra_deserialize) -- [Function `serialize`](#0x1_algebra_serialize) -- [Function `deserialize_internal`](#0x1_algebra_deserialize_internal) -- [Function `add_internal`](#0x1_algebra_add_internal) -- [Function `serialize_internal`](#0x1_algebra_serialize_internal) -- [Function `abort_unless_cryptography_algebra_natives_enabled`](#0x1_algebra_abort_unless_cryptography_algebra_natives_enabled) -- [Specification](#@Specification_0) - - [Function `deserialize_internal`](#@Specification_0_deserialize_internal) - - [Function `add_internal`](#@Specification_0_add_internal) - - [Function `serialize_internal`](#@Specification_0_serialize_internal) - - -
use 0x1::error;
-use 0x1::features;
-use 0x1::option;
-
- - - - - -## Struct `Element` - -This struct represents an element of an algebraic structure S. - - -
struct Element<S> has copy, drop
-
- - - -
-Fields - - -
-
-handle: u64 -
-
- -
-
- - -
- - - -## Function `add` - -Compute x + y for elements x and y of an algebraic structure S. - - -
public fun add<S>(x: &algebra::Element<S>, y: &algebra::Element<S>): algebra::Element<S>
-
- - - -
-Implementation - - -
public fun add<S>(x: &Element<S>, y: &Element<S>): Element<S> {
-    abort_unless_cryptography_algebra_natives_enabled();
-    Element<S> {
-        handle: add_internal<S>(x.handle, y.handle)
-    }
-}
-
- - - -
- - - -## Function `deserialize` - -Try deserializing a byte array to an element of an algebraic structure S using a given serialization format F. -Return none if the deserialization failed. - - -
public fun deserialize<S, F>(bytes: &vector<u8>): option::Option<algebra::Element<S>>
-
- - - -
-Implementation - - -
public fun deserialize<S, F>(bytes: &vector<u8>): Option<Element<S>> {
-    abort_unless_cryptography_algebra_natives_enabled();
-    let (succeeded, handle) = deserialize_internal<S, F>(bytes);
-    if (succeeded) {
-        some(Element<S> { handle })
-    } else {
-        none()
-    }
-}
-
- - - -
- - - -## Function `serialize` - -Serialize an element of an algebraic structure S to a byte array using a given serialization format F. - - -
public fun serialize<S, F>(element: &algebra::Element<S>): vector<u8>
-
- - - -
-Implementation - - -
public fun serialize<S, F>(element: &Element<S>): vector<u8> {
-    abort_unless_cryptography_algebra_natives_enabled();
-    serialize_internal<S, F>(element.handle)
-}
-
- - - -
- - - -## Function `deserialize_internal` - - - -
fun deserialize_internal<G, F>(bytes: &vector<u8>): (bool, u64)
-
- - - -
-Implementation - - -
native fun deserialize_internal<G, F>(bytes: &vector<u8>): (bool, u64);
-
- - - -
- - - -## Function `add_internal` - - - -
fun add_internal<F>(handle_1: u64, handle_2: u64): u64
-
- - - -
-Implementation - - -
native fun add_internal<F>(handle_1: u64, handle_2: u64): u64;
-
- - - -
- - - -## Function `serialize_internal` - - - -
fun serialize_internal<G, F>(handle: u64): vector<u8>
-
- - - -
-Implementation - - -
native fun serialize_internal<G, F>(handle: u64): vector<u8>;
-
- - - -
- - - -## Function `abort_unless_cryptography_algebra_natives_enabled` - - - -
fun abort_unless_cryptography_algebra_natives_enabled()
-
- - - -
-Implementation - - -
fun abort_unless_cryptography_algebra_natives_enabled() {
-    if (cryptography_algebra_enabled()) return;
-    abort(std::error::not_implemented(0))
-}
-
- - - -
- - - -## Specification - - - - -### Function `deserialize_internal` - - -
fun deserialize_internal<G, F>(bytes: &vector<u8>): (bool, u64)
-
- - - - -
pragma opaque;
-
- - - - - -### Function `add_internal` - - -
fun add_internal<F>(handle_1: u64, handle_2: u64): u64
-
- - - - -
pragma opaque;
-
- - - - - -### Function `serialize_internal` - - -
fun serialize_internal<G, F>(handle: u64): vector<u8>
-
- - - - -
pragma opaque;
-
- - -[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-stdlib/doc/algebra_bls12381.md b/aptos-move/framework/aptos-stdlib/doc/algebra_bls12381.md deleted file mode 100644 index 3c1b032c606bc..0000000000000 --- a/aptos-move/framework/aptos-stdlib/doc/algebra_bls12381.md +++ /dev/null @@ -1,81 +0,0 @@ - - - -# Module `0x1::algebra_bls12381` - -This module defines marker types, constants and test cases for working with BLS12-381 curves -using generic API defined in algebra.move. - -Currently supported BLS12-381 structures include field Fr. - - -- [Struct `Fr`](#0x1_algebra_bls12381_Fr) -- [Struct `FrFormatLsb`](#0x1_algebra_bls12381_FrFormatLsb) - - -
- - - - - -## Struct `Fr` - -The finite field $F_r$ that can be used as the scalar fields -for the groups $G_1$, $G_2$, $G_t$ in BLS12-381-based pairing. - - -
struct Fr
-
- - - -
-Fields - - -
-
-dummy_field: bool -
-
- -
-
- - -
- - - -## Struct `FrFormatLsb` - -A serialization format for Fr elements, -where an element is represented by a byte array b[] of size 32 with the least significant byte coming first. - -NOTE: the same scheme is also used in other implementations (e.g., ark-bls12-381-0.4.0, blst-0.3.7). - - -
struct FrFormatLsb
-
- - - -
-Fields - - -
-
-dummy_field: bool -
-
- -
-
- - -
- - -[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-stdlib/doc/bls12381_algebra.md b/aptos-move/framework/aptos-stdlib/doc/bls12381_algebra.md new file mode 100644 index 0000000000000..6d3af82f74346 --- /dev/null +++ b/aptos-move/framework/aptos-stdlib/doc/bls12381_algebra.md @@ -0,0 +1,631 @@ + + + +# Module `0x1::bls12381_algebra` + +This module defines marker types, constants and test cases for working with BLS12-381 curves +using the generic API defined in algebra.move. +See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11#name-bls-curves-for-the-128-bit- +for the full specification of BLS12-381 curves. + +Currently-supported BLS12-381 structures include Fq12, Fr, G1, G2 and Gt, +along with their widely-used serialization formats, +the pairing between G1, G2 and Gt, +and the hash-to-curve operations for G1 and G2 defined in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16. + +Other unimplemented BLS12-381 structures and serialization formats are also listed here, +as they help define some of the currently supported structures. +Their implementation may also be added in the future. + +Fq: the finite field $F_q$ used in BLS12-381 curves with a prime order $q$ equal to +0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab. + +FormatFqLsb: a serialization format for Fq elements, +where an element is represented by a byte array b[] of size 48 with the least significant byte (LSB) coming first. + +FormatFqMsb: a serialization format for Fq elements, +where an element is represented by a byte array b[] of size 48 with the most significant byte (MSB) coming first. + +Fq2: the finite field $F_{q^2}$ used in BLS12-381 curves, +which is an extension field of Fq, constructed as $F_{q^2}=F_q[u]/(u^2+1)$. + +FormatFq2LscLsb: a serialization format for Fq2 elements, +where an element in the form $(c_0+c_1\cdot u)$ is represented by a byte array b[] of size 96, +which is a concatenation of its coefficients serialized, with the least significant coefficient (LSC) coming first: +- b[0..48] is $c_0$ serialized using FormatFqLsb. +- b[48..96] is $c_1$ serialized using FormatFqLsb. + +FormatFq2MscMsb: a serialization format for Fq2 elements, +where an element in the form $(c_0+c_1\cdot u)$ is represented by a byte array b[] of size 96, +which is a concatenation of its coefficients serialized, with the most significant coefficient (MSC) coming first: +- b[0..48] is $c_1$ serialized using FormatFqLsb. +- b[48..96] is $c_0$ serialized using FormatFqLsb. + +Fq6: the finite field $F_{q^6}$ used in BLS12-381 curves, +which is an extension field of Fq2, constructed as $F_{q^6}=F_{q^2}[v]/(v^3-u-1)$. + +FormatFq6LscLsb: a serialization scheme for Fq6 elements, +where an element in the form $(c_0+c_1\cdot v+c_2\cdot v^2)$ is represented by a byte array b[] of size 288, +which is a concatenation of its coefficients serialized, with the least significant coefficient (LSC) coming first: +- b[0..96] is $c_0$ serialized using FormatFq2LscLsb. +- b[96..192] is $c_1$ serialized using FormatFq2LscLsb. +- b[192..288] is $c_2$ serialized using FormatFq2LscLsb. + +G1Full: a group constructed by the points on the BLS12-381 curve $E(F_q): y^2=x^3+4$ and the point at infinity, +under the elliptic curve point addition. +It contains the prime-order subgroup $G_1$ used in pairing. + +G2Full: a group constructed by the points on a curve $E'(F_{q^2}): y^2=x^3+4(u+1)$ and the point at infinity, +under the elliptic curve point addition. +It contains the prime-order subgroup $G_2$ used in pairing. + + +- [Struct `Fq12`](#0x1_bls12381_algebra_Fq12) +- [Struct `FormatFq12LscLsb`](#0x1_bls12381_algebra_FormatFq12LscLsb) +- [Struct `G1`](#0x1_bls12381_algebra_G1) +- [Struct `FormatG1Uncompr`](#0x1_bls12381_algebra_FormatG1Uncompr) +- [Struct `FormatG1Compr`](#0x1_bls12381_algebra_FormatG1Compr) +- [Struct `G2`](#0x1_bls12381_algebra_G2) +- [Struct `FormatG2Uncompr`](#0x1_bls12381_algebra_FormatG2Uncompr) +- [Struct `FormatG2Compr`](#0x1_bls12381_algebra_FormatG2Compr) +- [Struct `Gt`](#0x1_bls12381_algebra_Gt) +- [Struct `FormatGt`](#0x1_bls12381_algebra_FormatGt) +- [Struct `Fr`](#0x1_bls12381_algebra_Fr) +- [Struct `FormatFrLsb`](#0x1_bls12381_algebra_FormatFrLsb) +- [Struct `FormatFrMsb`](#0x1_bls12381_algebra_FormatFrMsb) +- [Struct `HashG1XmdSha256SswuRo`](#0x1_bls12381_algebra_HashG1XmdSha256SswuRo) +- [Struct `HashG2XmdSha256SswuRo`](#0x1_bls12381_algebra_HashG2XmdSha256SswuRo) + + +
+ + + + + +## Struct `Fq12` + +The finite field $F_{q^12}$ used in BLS12-381 curves, +which is an extension field of Fq6 (defined in the module documentation), constructed as $F_{q^12}=F_{q^6}[w]/(w^2-v)$. + + +
struct Fq12
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatFq12LscLsb` + +A serialization scheme for Fq12 elements, +where an element $(c_0+c_1\cdot w)$ is represented by a byte array b[] of size 576, +which is a concatenation of its coefficients serialized, with the least significant coefficient (LSC) coming first. +- b[0..288] is $c_0$ serialized using FormatFq6LscLsb (defined in the module documentation). +- b[288..576] is $c_1$ serialized using FormatFq6LscLsb. + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + + +
struct FormatFq12LscLsb
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `G1` + +The group $G_1$ in BLS12-381-based pairing $G_1 \times G_2 \rightarrow G_t$. +It is a subgroup of G1Full (defined in the module documentation) with a prime order $r$ +equal to 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. +(so Fr is the associated scalar field). + + +
struct G1
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatG1Uncompr` + +A serialization scheme for G1 elements derived from +https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + +Below is the serialization procedure that takes a G1 element p and outputs a byte array of size 96. +1. Let (x,y) be the coordinates of p if p is on the curve, or (0,0) otherwise. +1. Serialize x and y into b_x[] and b_y[] respectively using FormatFqMsb (defined in the module documentation). +1. Concatenate b_x[] and b_y[] into b[]. +1. If p is the point at infinity, set the infinity bit: b[0]: = b[0] | 0x40. +1. Return b[]. + +Below is the deserialization procedure that takes a byte array b[] and outputs either a G1 element or none. +1. If the size of b[] is not 96, return none. +1. Compute the compression flag as b[0] & 0x80 != 0. +1. If the compression flag is true, return none. +1. Compute the infinity flag as b[0] & 0x40 != 0. +1. If the infinity flag is set, return the point at infinity. +1. Deserialize [b[0] & 0x1f, b[1], ..., b[47]] to x using FormatFqMsb. If x is none, return none. +1. Deserialize [b[48], ..., b[95]] to y using FormatFqMsb. If y is none, return none. +1. Check if (x,y) is on curve E. If not, return none. +1. Check if (x,y) is in the subgroup of order r. If not, return none. +1. Return (x,y). + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + + +
struct FormatG1Uncompr
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatG1Compr` + +A serialization scheme for G1 elements derived from +https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + +Below is the serialization procedure that takes a G1 element p and outputs a byte array of size 48. +1. Let (x,y) be the coordinates of p if p is on the curve, or (0,0) otherwise. +1. Serialize x into b[] using FormatFqMsb (defined in the module documentation). +1. Set the compression bit: b[0] := b[0] | 0x80. +1. If p is the point at infinity, set the infinity bit: b[0]: = b[0] | 0x40. +1. If y > -y, set the lexicographical flag: b[0] := b[0] | 0x20. +1. Return b[]. + +Below is the deserialization procedure that takes a byte array b[] and outputs either a G1 element or none. +1. If the size of b[] is not 48, return none. +1. Compute the compression flag as b[0] & 0x80 != 0. +1. If the compression flag is false, return none. +1. Compute the infinity flag as b[0] & 0x40 != 0. +1. If the infinity flag is set, return the point at infinity. +1. Compute the lexicographical flag as b[0] & 0x20 != 0. +1. Deserialize [b[0] & 0x1f, b[1], ..., b[47]] to x using FormatFqMsb. If x is none, return none. +1. Solve the curve equation with x for y. If no such y exists, return none. +1. Let y' be max(y,-y) if the lexicographical flag is set, or min(y,-y) otherwise. +1. Check if (x,y') is in the subgroup of order r. If not, return none. +1. Return (x,y'). + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + + +
struct FormatG1Compr
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `G2` + +The group $G_2$ in BLS12-381-based pairing $G_1 \times G_2 \rightarrow G_t$. +It is a subgroup of G2Full (defined in the module documentation) with a prime order $r$ equal to +0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. +(so Fr is the scalar field). + + +
struct G2
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatG2Uncompr` + +A serialization scheme for G2 elements derived from +https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + +Below is the serialization procedure that takes a G2 element p and outputs a byte array of size 192. +1. Let (x,y) be the coordinates of p if p is on the curve, or (0,0) otherwise. +1. Serialize x and y into b_x[] and b_y[] respectively using FormatFq2MscMsb (defined in the module documentation). +1. Concatenate b_x[] and b_y[] into b[]. +1. If p is the point at infinity, set the infinity bit in b[]: b[0]: = b[0] | 0x40. +1. Return b[]. + +Below is the deserialization procedure that takes a byte array b[] and outputs either a G2 element or none. +1. If the size of b[] is not 192, return none. +1. Compute the compression flag as b[0] & 0x80 != 0. +1. If the compression flag is true, return none. +1. Compute the infinity flag as b[0] & 0x40 != 0. +1. If the infinity flag is set, return the point at infinity. +1. Deserialize [b[0] & 0x1f, ..., b[95]] to x using FormatFq2MscMsb. If x is none, return none. +1. Deserialize [b[96], ..., b[191]] to y using FormatFq2MscMsb. If y is none, return none. +1. Check if (x,y) is on the curve E'. If not, return none. +1. Check if (x,y) is in the subgroup of order r. If not, return none. +1. Return (x,y). + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + + +
struct FormatG2Uncompr
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatG2Compr` + +A serialization scheme for G2 elements derived from +https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + +Below is the serialization procedure that takes a G2 element p and outputs a byte array of size 96. +1. Let (x,y) be the coordinates of p if p is on the curve, or (0,0) otherwise. +1. Serialize x into b[] using FormatFq2MscMsb (defined in the module documentation). +1. Set the compression bit: b[0] := b[0] | 0x80. +1. If p is the point at infinity, set the infinity bit: b[0]: = b[0] | 0x40. +1. If y > -y, set the lexicographical flag: b[0] := b[0] | 0x20. +1. Return b[]. + +Below is the deserialization procedure that takes a byte array b[] and outputs either a G2 element or none. +1. If the size of b[] is not 96, return none. +1. Compute the compression flag as b[0] & 0x80 != 0. +1. If the compression flag is false, return none. +1. Compute the infinity flag as b[0] & 0x40 != 0. +1. If the infinity flag is set, return the point at infinity. +1. Compute the lexicographical flag as b[0] & 0x20 != 0. +1. Deserialize [b[0] & 0x1f, b[1], ..., b[95]] to x using FormatFq2MscMsb. If x is none, return none. +1. Solve the curve equation with x for y. If no such y exists, return none. +1. Let y' be max(y,-y) if the lexicographical flag is set, or min(y,-y) otherwise. +1. Check if (x,y') is in the subgroup of order r. If not, return none. +1. Return (x,y'). + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + + +
struct FormatG2Compr
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `Gt` + +The group $G_t$ in BLS12-381-based pairing $G_1 \times G_2 \rightarrow G_t$. +It is a multiplicative subgroup of Fq12, +with a prime order $r$ equal to 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. +(so Fr is the scalar field). +The identity of Gt is 1. + + +
struct Gt
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatGt` + +A serialization scheme for Gt elements. + +To serialize, it treats a Gt element p as an Fq12 element and serialize it using FormatFq12LscLsb. + +To deserialize, it uses FormatFq12LscLsb to try deserializing to an Fq12 element then test the membership in Gt. + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + + +
struct FormatGt
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `Fr` + +The finite field $F_r$ that can be used as the scalar fields +associated with the groups $G_1$, $G_2$, $G_t$ in BLS12-381-based pairing. + + +
struct Fr
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatFrLsb` + +A serialization format for Fr elements, +where an element is represented by a byte array b[] of size 32 with the least significant byte (LSB) coming first. + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0, blst-0.3.7. + + +
struct FormatFrLsb
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `FormatFrMsb` + +A serialization scheme for Fr elements, +where an element is represented by a byte array b[] of size 32 with the most significant byte (MSB) coming first. + +NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0, blst-0.3.7. + + +
struct FormatFrMsb
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `HashG1XmdSha256SswuRo` + +The hash-to-curve suite BLS12381G1_XMD:SHA-256_SSWU_RO_ that hashes a byte array into G1 elements. + +Full specification is defined in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#name-bls12-381-g1. + + +
struct HashG1XmdSha256SswuRo
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `HashG2XmdSha256SswuRo` + +The hash-to-curve suite BLS12381G2_XMD:SHA-256_SSWU_RO_ that hashes a byte array into G2 elements. + +Full specification is defined in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#name-bls12-381-g2. + + +
struct HashG2XmdSha256SswuRo
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + +[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-stdlib/doc/crypto_algebra.md b/aptos-move/framework/aptos-stdlib/doc/crypto_algebra.md new file mode 100644 index 0000000000000..0844d53c2b8ed --- /dev/null +++ b/aptos-move/framework/aptos-stdlib/doc/crypto_algebra.md @@ -0,0 +1,1722 @@ + + + +# Module `0x1::crypto_algebra` + +This module provides generic structs/functions for operations of algebraic structures (e.g. fields and groups), +which can be used to build generic cryptographic schemes atop. +E.g., a Groth16 ZK proof verifier can be built to work over any pairing supported in this module. + +In general, every structure implements basic operations like (de)serialization, equality check, random sampling. + +A group may also implement the following operations. (Additive group notation is assumed.) +- order() for getting the group order. +- zero() for getting the group identity. +- one() for getting the group generator (if exists). +- neg() for group element inversion. +- add() for group operation (i.e., a group addition). +- sub() for group element subtraction. +- double() for efficient doubling. +- scalar_mul() for group scalar multiplication. +- multi_scalar_mul() for efficient group multi-scalar multiplication. +- hash_to() for hash-to-group. + +A field may also implement the following operations. +- zero() for getting the field additive identity. +- one() for getting the field multiplicative identity. +- add() for field addition. +- sub() for field subtraction. +- mul() for field multiplication. +- div() for field division. +- neg() for field negation. +- inv() for field inversion. +- sqr() for efficient field element squaring. +- from_u64() for quick conversion from u64 to field element. + +For 3 groups that admit a bilinear map, pairing() and multi_pairing() may be implemented. + +For a subset/superset relationship between 2 structures, upcast() and downcast() may be implemented. +E.g., in BLS12-381 pairing, since Gt is a subset of Fq12, +upcast<Gt, Fq12>() and downcast<Fq12, Gt>() will be supported. + +See *_algebra.move for currently implemented algebraic structures. + + +- [Struct `Element`](#0x1_crypto_algebra_Element) +- [Constants](#@Constants_0) +- [Function `eq`](#0x1_crypto_algebra_eq) +- [Function `from_u64`](#0x1_crypto_algebra_from_u64) +- [Function `zero`](#0x1_crypto_algebra_zero) +- [Function `one`](#0x1_crypto_algebra_one) +- [Function `neg`](#0x1_crypto_algebra_neg) +- [Function `add`](#0x1_crypto_algebra_add) +- [Function `sub`](#0x1_crypto_algebra_sub) +- [Function `mul`](#0x1_crypto_algebra_mul) +- [Function `div`](#0x1_crypto_algebra_div) +- [Function `sqr`](#0x1_crypto_algebra_sqr) +- [Function `inv`](#0x1_crypto_algebra_inv) +- [Function `double`](#0x1_crypto_algebra_double) +- [Function `multi_scalar_mul`](#0x1_crypto_algebra_multi_scalar_mul) +- [Function `scalar_mul`](#0x1_crypto_algebra_scalar_mul) +- [Function `multi_pairing`](#0x1_crypto_algebra_multi_pairing) +- [Function `pairing`](#0x1_crypto_algebra_pairing) +- [Function `deserialize`](#0x1_crypto_algebra_deserialize) +- [Function `serialize`](#0x1_crypto_algebra_serialize) +- [Function `order`](#0x1_crypto_algebra_order) +- [Function `upcast`](#0x1_crypto_algebra_upcast) +- [Function `downcast`](#0x1_crypto_algebra_downcast) +- [Function `hash_to`](#0x1_crypto_algebra_hash_to) +- [Function `abort_unless_cryptography_algebra_natives_enabled`](#0x1_crypto_algebra_abort_unless_cryptography_algebra_natives_enabled) +- [Function `handles_from_elements`](#0x1_crypto_algebra_handles_from_elements) +- [Function `add_internal`](#0x1_crypto_algebra_add_internal) +- [Function `deserialize_internal`](#0x1_crypto_algebra_deserialize_internal) +- [Function `div_internal`](#0x1_crypto_algebra_div_internal) +- [Function `double_internal`](#0x1_crypto_algebra_double_internal) +- [Function `downcast_internal`](#0x1_crypto_algebra_downcast_internal) +- [Function `from_u64_internal`](#0x1_crypto_algebra_from_u64_internal) +- [Function `eq_internal`](#0x1_crypto_algebra_eq_internal) +- [Function `hash_to_internal`](#0x1_crypto_algebra_hash_to_internal) +- [Function `inv_internal`](#0x1_crypto_algebra_inv_internal) +- [Function `mul_internal`](#0x1_crypto_algebra_mul_internal) +- [Function `multi_pairing_internal`](#0x1_crypto_algebra_multi_pairing_internal) +- [Function `multi_scalar_mul_internal`](#0x1_crypto_algebra_multi_scalar_mul_internal) +- [Function `neg_internal`](#0x1_crypto_algebra_neg_internal) +- [Function `one_internal`](#0x1_crypto_algebra_one_internal) +- [Function `order_internal`](#0x1_crypto_algebra_order_internal) +- [Function `pairing_internal`](#0x1_crypto_algebra_pairing_internal) +- [Function `scalar_mul_internal`](#0x1_crypto_algebra_scalar_mul_internal) +- [Function `serialize_internal`](#0x1_crypto_algebra_serialize_internal) +- [Function `sqr_internal`](#0x1_crypto_algebra_sqr_internal) +- [Function `sub_internal`](#0x1_crypto_algebra_sub_internal) +- [Function `upcast_internal`](#0x1_crypto_algebra_upcast_internal) +- [Function `zero_internal`](#0x1_crypto_algebra_zero_internal) +- [Specification](#@Specification_1) + - [Function `add_internal`](#@Specification_1_add_internal) + - [Function `deserialize_internal`](#@Specification_1_deserialize_internal) + - [Function `div_internal`](#@Specification_1_div_internal) + - [Function `double_internal`](#@Specification_1_double_internal) + - [Function `downcast_internal`](#@Specification_1_downcast_internal) + - [Function `from_u64_internal`](#@Specification_1_from_u64_internal) + - [Function `eq_internal`](#@Specification_1_eq_internal) + - [Function `hash_to_internal`](#@Specification_1_hash_to_internal) + - [Function `inv_internal`](#@Specification_1_inv_internal) + - [Function `mul_internal`](#@Specification_1_mul_internal) + - [Function `multi_pairing_internal`](#@Specification_1_multi_pairing_internal) + - [Function `multi_scalar_mul_internal`](#@Specification_1_multi_scalar_mul_internal) + - [Function `neg_internal`](#@Specification_1_neg_internal) + - [Function `one_internal`](#@Specification_1_one_internal) + - [Function `order_internal`](#@Specification_1_order_internal) + - [Function `pairing_internal`](#@Specification_1_pairing_internal) + - [Function `scalar_mul_internal`](#@Specification_1_scalar_mul_internal) + - [Function `serialize_internal`](#@Specification_1_serialize_internal) + - [Function `sqr_internal`](#@Specification_1_sqr_internal) + - [Function `sub_internal`](#@Specification_1_sub_internal) + - [Function `upcast_internal`](#@Specification_1_upcast_internal) + - [Function `zero_internal`](#@Specification_1_zero_internal) + + +
use 0x1::error;
+use 0x1::features;
+use 0x1::option;
+
+ + + + + +## Struct `Element` + +This struct represents an element of a structure S. + + +
struct Element<S> has copy, drop
+
+ + + +
+Fields + + +
+
+handle: u64 +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const E_NON_EQUAL_LENGTHS: u64 = 2;
+
+ + + + + + + +
const E_NOT_IMPLEMENTED: u64 = 1;
+
+ + + + + +## Function `eq` + +Check if x == y for elements x and y of a structure S. + + +
public fun eq<S>(x: &crypto_algebra::Element<S>, y: &crypto_algebra::Element<S>): bool
+
+ + + +
+Implementation + + +
public fun eq<S>(x: &Element<S>, y: &Element<S>): bool {
+    abort_unless_cryptography_algebra_natives_enabled();
+    eq_internal<S>(x.handle, y.handle)
+}
+
+ + + +
+ + + +## Function `from_u64` + +Convert a u64 to an element of a structure S. + + +
public fun from_u64<S>(value: u64): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun from_u64<S>(value: u64): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: from_u64_internal<S>(value)
+    }
+}
+
+ + + +
+ + + +## Function `zero` + +Return the additive identity of field S, or the identity of group S. + + +
public fun zero<S>(): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun zero<S>(): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: zero_internal<S>()
+    }
+}
+
+ + + +
+ + + +## Function `one` + +Return the multiplicative identity of field S, or a fixed generator of group S. + + +
public fun one<S>(): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun one<S>(): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: one_internal<S>()
+    }
+}
+
+ + + +
+ + + +## Function `neg` + +Compute -x for an element x of a structure S. + + +
public fun neg<S>(x: &crypto_algebra::Element<S>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun neg<S>(x: &Element<S>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: neg_internal<S>(x.handle)
+    }
+}
+
+ + + +
+ + + +## Function `add` + +Compute x + y for elements x and y of structure S. + + +
public fun add<S>(x: &crypto_algebra::Element<S>, y: &crypto_algebra::Element<S>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun add<S>(x: &Element<S>, y: &Element<S>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: add_internal<S>(x.handle, y.handle)
+    }
+}
+
+ + + +
+ + + +## Function `sub` + +Compute x - y for elements x and y of a structure S. + + +
public fun sub<S>(x: &crypto_algebra::Element<S>, y: &crypto_algebra::Element<S>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun sub<S>(x: &Element<S>, y: &Element<S>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: sub_internal<S>(x.handle, y.handle)
+    }
+}
+
+ + + +
+ + + +## Function `mul` + +Compute x * y for elements x and y of a structure S. + + +
public fun mul<S>(x: &crypto_algebra::Element<S>, y: &crypto_algebra::Element<S>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun mul<S>(x: &Element<S>, y: &Element<S>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: mul_internal<S>(x.handle, y.handle)
+    }
+}
+
+ + + +
+ + + +## Function `div` + +Try computing x / y for elements x and y of a structure S. +Return none if y does not have a multiplicative inverse in the structure S +(e.g., when S is a field, and y is zero). + + +
public fun div<S>(x: &crypto_algebra::Element<S>, y: &crypto_algebra::Element<S>): option::Option<crypto_algebra::Element<S>>
+
+ + + +
+Implementation + + +
public fun div<S>(x: &Element<S>, y: &Element<S>): Option<Element<S>> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    let (succ, handle) = div_internal<S>(x.handle, y.handle);
+    if (succ) {
+        some(Element<S> { handle })
+    } else {
+        none()
+    }
+}
+
+ + + +
+ + + +## Function `sqr` + +Compute x^2 for an element x of a structure S. Faster and cheaper than mul(x, x). + + +
public fun sqr<S>(x: &crypto_algebra::Element<S>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun sqr<S>(x: &Element<S>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: sqr_internal<S>(x.handle)
+    }
+}
+
+ + + +
+ + + +## Function `inv` + +Try computing x^(-1) for an element x of a structure S. +Return none if x does not have a multiplicative inverse in the structure S +(e.g., when S is a field, and x is zero). + + +
public fun inv<S>(x: &crypto_algebra::Element<S>): option::Option<crypto_algebra::Element<S>>
+
+ + + +
+Implementation + + +
public fun inv<S>(x: &Element<S>): Option<Element<S>> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    let (succeeded, handle) = inv_internal<S>(x.handle);
+    if (succeeded) {
+        let scalar = Element<S> { handle };
+        some(scalar)
+    } else {
+        none()
+    }
+}
+
+ + + +
+ + + +## Function `double` + +Compute 2*P for an element P of a structure S. Faster and cheaper than add(P, P). + + +
public fun double<S>(element_p: &crypto_algebra::Element<S>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun double<S>(element_p: &Element<S>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<S> {
+        handle: double_internal<S>(element_p.handle)
+    }
+}
+
+ + + +
+ + + +## Function `multi_scalar_mul` + +Compute k[0]*P[0]+...+k[n-1]*P[n-1], where +P[] are n elements of group G represented by parameter elements, and +k[] are n elements of the scalarfield S of group G represented by parameter scalars. + +Abort with code std::error::invalid_argument(E_NON_EQUAL_LENGTHS) if the sizes of elements and scalars do not match. + + +
public fun multi_scalar_mul<G, S>(elements: &vector<crypto_algebra::Element<G>>, scalars: &vector<crypto_algebra::Element<S>>): crypto_algebra::Element<G>
+
+ + + +
+Implementation + + +
public fun multi_scalar_mul<G, S>(elements: &vector<Element<G>>, scalars: &vector<Element<S>>): Element<G> {
+    let element_handles = handles_from_elements(elements);
+    let scalar_handles = handles_from_elements(scalars);
+    Element<G> {
+        handle: multi_scalar_mul_internal<G, S>(element_handles, scalar_handles)
+    }
+}
+
+ + + +
+ + + +## Function `scalar_mul` + +Compute k*P, where P is an element of a group G and k is an element of the scalar field S associated to the group G. + + +
public fun scalar_mul<G, S>(element_p: &crypto_algebra::Element<G>, scalar_k: &crypto_algebra::Element<S>): crypto_algebra::Element<G>
+
+ + + +
+Implementation + + +
public fun scalar_mul<G, S>(element_p: &Element<G>, scalar_k: &Element<S>): Element<G> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<G> {
+        handle: scalar_mul_internal<G, S>(element_p.handle, scalar_k.handle)
+    }
+}
+
+ + + +
+ + + +## Function `multi_pairing` + +Efficiently compute e(P[0],Q[0])+...+e(P[n-1],Q[n-1]), +where e: (G1,G2) -> (Gt) is the pairing function from groups (G1,G2) to group Gt, +P[] are n elements of group G1 represented by parameter g1_elements, and +Q[] are n elements of group G2 represented by parameter g2_elements. + +Abort with code std::error::invalid_argument(E_NON_EQUAL_LENGTHS) if the sizes of g1_elements and g2_elements do not match. + +NOTE: we are viewing the target group Gt of the pairing as an additive group, +rather than a multiplicative one (which is typically the case). + + +
public fun multi_pairing<G1, G2, Gt>(g1_elements: &vector<crypto_algebra::Element<G1>>, g2_elements: &vector<crypto_algebra::Element<G2>>): crypto_algebra::Element<Gt>
+
+ + + +
+Implementation + + +
public fun multi_pairing<G1,G2,Gt>(g1_elements: &vector<Element<G1>>, g2_elements: &vector<Element<G2>>): Element<Gt> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    let g1_handles = handles_from_elements(g1_elements);
+    let g2_handles = handles_from_elements(g2_elements);
+    Element<Gt> {
+        handle: multi_pairing_internal<G1,G2,Gt>(g1_handles, g2_handles)
+    }
+}
+
+ + + +
+ + + +## Function `pairing` + +Compute the pairing function (a.k.a., bilinear map) on a G1 element and a G2 element. +Return an element in the target group Gt. + + +
public fun pairing<G1, G2, Gt>(element_1: &crypto_algebra::Element<G1>, element_2: &crypto_algebra::Element<G2>): crypto_algebra::Element<Gt>
+
+ + + +
+Implementation + + +
public fun pairing<G1,G2,Gt>(element_1: &Element<G1>, element_2: &Element<G2>): Element<Gt> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<Gt> {
+        handle: pairing_internal<G1,G2,Gt>(element_1.handle, element_2.handle)
+    }
+}
+
+ + + +
+ + + +## Function `deserialize` + +Try deserializing a byte array to an element of an algebraic structure S using a given serialization format F. +Return none if the deserialization failed. + + +
public fun deserialize<S, F>(bytes: &vector<u8>): option::Option<crypto_algebra::Element<S>>
+
+ + + +
+Implementation + + +
public fun deserialize<S, F>(bytes: &vector<u8>): Option<Element<S>> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    let (succeeded, handle) = deserialize_internal<S, F>(bytes);
+    if (succeeded) {
+        some(Element<S> { handle })
+    } else {
+        none()
+    }
+}
+
+ + + +
+ + + +## Function `serialize` + +Serialize an element of an algebraic structure S to a byte array using a given serialization format F. + + +
public fun serialize<S, F>(element: &crypto_algebra::Element<S>): vector<u8>
+
+ + + +
+Implementation + + +
public fun serialize<S, F>(element: &Element<S>): vector<u8> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    serialize_internal<S, F>(element.handle)
+}
+
+ + + +
+ + + +## Function `order` + +Get the order of structure S, a big integer little-endian encoded as a byte array. + + +
public fun order<S>(): vector<u8>
+
+ + + +
+Implementation + + +
public fun order<S>(): vector<u8> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    order_internal<S>()
+}
+
+ + + +
+ + + +## Function `upcast` + +Cast an element of a structure S to a parent structure L. + + +
public fun upcast<S, L>(element: &crypto_algebra::Element<S>): crypto_algebra::Element<L>
+
+ + + +
+Implementation + + +
public fun upcast<S,L>(element: &Element<S>): Element<L> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element<L> {
+        handle: upcast_internal<S,L>(element.handle)
+    }
+}
+
+ + + +
+ + + +## Function `downcast` + +Try casting an element x of a structure L to a sub-structure S. +Return none if x is not a member of S. + +NOTE: Membership check in S is performed inside, which can be expensive, depending on the structures L and S. + + +
public fun downcast<L, S>(element_x: &crypto_algebra::Element<L>): option::Option<crypto_algebra::Element<S>>
+
+ + + +
+Implementation + + +
public fun downcast<L,S>(element_x: &Element<L>): Option<Element<S>> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    let (succ, new_handle) = downcast_internal<L,S>(element_x.handle);
+    if (succ) {
+        some(Element<S> { handle: new_handle })
+    } else {
+        none()
+    }
+}
+
+ + + +
+ + + +## Function `hash_to` + +Hash an arbitrary-length byte array msg into structure S with a domain separation tag dst +using the given hash-to-structure suite H. + +NOTE: some hashing methods do not accept a dst and will abort if a non-empty one is provided. + + +
public fun hash_to<S, H>(dst: &vector<u8>, msg: &vector<u8>): crypto_algebra::Element<S>
+
+ + + +
+Implementation + + +
public fun hash_to<S, H>(dst: &vector<u8>, msg: &vector<u8>): Element<S> {
+    abort_unless_cryptography_algebra_natives_enabled();
+    Element {
+        handle: hash_to_internal<S, H>(dst, msg)
+    }
+}
+
+ + + +
+ + + +## Function `abort_unless_cryptography_algebra_natives_enabled` + + + +
fun abort_unless_cryptography_algebra_natives_enabled()
+
+ + + +
+Implementation + + +
fun abort_unless_cryptography_algebra_natives_enabled() {
+    if (features::cryptography_algebra_enabled()) return;
+    abort(std::error::not_implemented(0))
+}
+
+ + + +
+ + + +## Function `handles_from_elements` + + + +
fun handles_from_elements<S>(elements: &vector<crypto_algebra::Element<S>>): vector<u64>
+
+ + + +
+Implementation + + +
fun handles_from_elements<S>(elements: &vector<Element<S>>): vector<u64> {
+    let num_elements = std::vector::length(elements);
+    let element_handles = std::vector::empty();
+    let i = 0;
+    while (i < num_elements) {
+        std::vector::push_back(&mut element_handles, std::vector::borrow(elements, i).handle);
+        i = i + 1;
+    };
+    element_handles
+}
+
+ + + +
+ + + +## Function `add_internal` + + + +
fun add_internal<S>(handle_1: u64, handle_2: u64): u64
+
+ + + +
+Implementation + + +
native fun add_internal<S>(handle_1: u64, handle_2: u64): u64;
+
+ + + +
+ + + +## Function `deserialize_internal` + + + +
fun deserialize_internal<S, F>(bytes: &vector<u8>): (bool, u64)
+
+ + + +
+Implementation + + +
native fun deserialize_internal<S, F>(bytes: &vector<u8>): (bool, u64);
+
+ + + +
+ + + +## Function `div_internal` + + + +
fun div_internal<F>(handle_1: u64, handle_2: u64): (bool, u64)
+
+ + + +
+Implementation + + +
native fun div_internal<F>(handle_1: u64, handle_2: u64): (bool, u64);
+
+ + + +
+ + + +## Function `double_internal` + + + +
fun double_internal<G>(element_handle: u64): u64
+
+ + + +
+Implementation + + +
native fun double_internal<G>(element_handle: u64): u64;
+
+ + + +
+ + + +## Function `downcast_internal` + + + +
fun downcast_internal<L, S>(handle: u64): (bool, u64)
+
+ + + +
+Implementation + + +
native fun downcast_internal<L,S>(handle: u64): (bool, u64);
+
+ + + +
+ + + +## Function `from_u64_internal` + + + +
fun from_u64_internal<S>(value: u64): u64
+
+ + + +
+Implementation + + +
native fun from_u64_internal<S>(value: u64): u64;
+
+ + + +
+ + + +## Function `eq_internal` + + + +
fun eq_internal<S>(handle_1: u64, handle_2: u64): bool
+
+ + + +
+Implementation + + +
native fun eq_internal<S>(handle_1: u64, handle_2: u64): bool;
+
+ + + +
+ + + +## Function `hash_to_internal` + + + +
fun hash_to_internal<S, H>(dst: &vector<u8>, bytes: &vector<u8>): u64
+
+ + + +
+Implementation + + +
native fun hash_to_internal<S, H>(dst: &vector<u8>, bytes: &vector<u8>): u64;
+
+ + + +
+ + + +## Function `inv_internal` + + + +
fun inv_internal<F>(handle: u64): (bool, u64)
+
+ + + +
+Implementation + + +
native fun inv_internal<F>(handle: u64): (bool, u64);
+
+ + + +
+ + + +## Function `mul_internal` + + + +
fun mul_internal<F>(handle_1: u64, handle_2: u64): u64
+
+ + + +
+Implementation + + +
native fun mul_internal<F>(handle_1: u64, handle_2: u64): u64;
+
+ + + +
+ + + +## Function `multi_pairing_internal` + + + +
fun multi_pairing_internal<G1, G2, Gt>(g1_handles: vector<u64>, g2_handles: vector<u64>): u64
+
+ + + +
+Implementation + + +
native fun multi_pairing_internal<G1,G2,Gt>(g1_handles: vector<u64>, g2_handles: vector<u64>): u64;
+
+ + + +
+ + + +## Function `multi_scalar_mul_internal` + + + +
fun multi_scalar_mul_internal<G, S>(element_handles: vector<u64>, scalar_handles: vector<u64>): u64
+
+ + + +
+Implementation + + +
native fun multi_scalar_mul_internal<G, S>(element_handles: vector<u64>, scalar_handles: vector<u64>): u64;
+
+ + + +
+ + + +## Function `neg_internal` + + + +
fun neg_internal<F>(handle: u64): u64
+
+ + + +
+Implementation + + +
native fun neg_internal<F>(handle: u64): u64;
+
+ + + +
+ + + +## Function `one_internal` + + + +
fun one_internal<S>(): u64
+
+ + + +
+Implementation + + +
native fun one_internal<S>(): u64;
+
+ + + +
+ + + +## Function `order_internal` + + + +
fun order_internal<G>(): vector<u8>
+
+ + + +
+Implementation + + +
native fun order_internal<G>(): vector<u8>;
+
+ + + +
+ + + +## Function `pairing_internal` + + + +
fun pairing_internal<G1, G2, Gt>(g1_handle: u64, g2_handle: u64): u64
+
+ + + +
+Implementation + + +
native fun pairing_internal<G1,G2,Gt>(g1_handle: u64, g2_handle: u64): u64;
+
+ + + +
+ + + +## Function `scalar_mul_internal` + + + +
fun scalar_mul_internal<G, S>(element_handle: u64, scalar_handle: u64): u64
+
+ + + +
+Implementation + + +
native fun scalar_mul_internal<G, S>(element_handle: u64, scalar_handle: u64): u64;
+
+ + + +
+ + + +## Function `serialize_internal` + + + +
fun serialize_internal<S, F>(handle: u64): vector<u8>
+
+ + + +
+Implementation + + +
native fun serialize_internal<S, F>(handle: u64): vector<u8>;
+
+ + + +
+ + + +## Function `sqr_internal` + + + +
fun sqr_internal<G>(handle: u64): u64
+
+ + + +
+Implementation + + +
native fun sqr_internal<G>(handle: u64): u64;
+
+ + + +
+ + + +## Function `sub_internal` + + + +
fun sub_internal<G>(handle_1: u64, handle_2: u64): u64
+
+ + + +
+Implementation + + +
native fun sub_internal<G>(handle_1: u64, handle_2: u64): u64;
+
+ + + +
+ + + +## Function `upcast_internal` + + + +
fun upcast_internal<S, L>(handle: u64): u64
+
+ + + +
+Implementation + + +
native fun upcast_internal<S,L>(handle: u64): u64;
+
+ + + +
+ + + +## Function `zero_internal` + + + +
fun zero_internal<S>(): u64
+
+ + + +
+Implementation + + +
native fun zero_internal<S>(): u64;
+
+ + + +
+ + + +## Specification + + + + +### Function `add_internal` + + +
fun add_internal<S>(handle_1: u64, handle_2: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `deserialize_internal` + + +
fun deserialize_internal<S, F>(bytes: &vector<u8>): (bool, u64)
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `div_internal` + + +
fun div_internal<F>(handle_1: u64, handle_2: u64): (bool, u64)
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `double_internal` + + +
fun double_internal<G>(element_handle: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `downcast_internal` + + +
fun downcast_internal<L, S>(handle: u64): (bool, u64)
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `from_u64_internal` + + +
fun from_u64_internal<S>(value: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `eq_internal` + + +
fun eq_internal<S>(handle_1: u64, handle_2: u64): bool
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `hash_to_internal` + + +
fun hash_to_internal<S, H>(dst: &vector<u8>, bytes: &vector<u8>): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `inv_internal` + + +
fun inv_internal<F>(handle: u64): (bool, u64)
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `mul_internal` + + +
fun mul_internal<F>(handle_1: u64, handle_2: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `multi_pairing_internal` + + +
fun multi_pairing_internal<G1, G2, Gt>(g1_handles: vector<u64>, g2_handles: vector<u64>): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `multi_scalar_mul_internal` + + +
fun multi_scalar_mul_internal<G, S>(element_handles: vector<u64>, scalar_handles: vector<u64>): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `neg_internal` + + +
fun neg_internal<F>(handle: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `one_internal` + + +
fun one_internal<S>(): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `order_internal` + + +
fun order_internal<G>(): vector<u8>
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `pairing_internal` + + +
fun pairing_internal<G1, G2, Gt>(g1_handle: u64, g2_handle: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `scalar_mul_internal` + + +
fun scalar_mul_internal<G, S>(element_handle: u64, scalar_handle: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `serialize_internal` + + +
fun serialize_internal<S, F>(handle: u64): vector<u8>
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `sqr_internal` + + +
fun sqr_internal<G>(handle: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `sub_internal` + + +
fun sub_internal<G>(handle_1: u64, handle_2: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `upcast_internal` + + +
fun upcast_internal<S, L>(handle: u64): u64
+
+ + + + +
pragma opaque;
+
+ + + + + +### Function `zero_internal` + + +
fun zero_internal<S>(): u64
+
+ + + + +
pragma opaque;
+
+ + +[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-stdlib/doc/overview.md b/aptos-move/framework/aptos-stdlib/doc/overview.md index e056a60f1981a..c5a60915286a5 100644 --- a/aptos-move/framework/aptos-stdlib/doc/overview.md +++ b/aptos-move/framework/aptos-stdlib/doc/overview.md @@ -12,15 +12,15 @@ This is the reference documentation of the Aptos standard library. ## Index -- [`0x1::algebra`](algebra.md#0x1_algebra) -- [`0x1::algebra_bls12381`](algebra_bls12381.md#0x1_algebra_bls12381) - [`0x1::any`](any.md#0x1_any) - [`0x1::aptos_hash`](hash.md#0x1_aptos_hash) - [`0x1::big_vector`](big_vector.md#0x1_big_vector) - [`0x1::bls12381`](bls12381.md#0x1_bls12381) +- [`0x1::bls12381_algebra`](bls12381_algebra.md#0x1_bls12381_algebra) - [`0x1::capability`](capability.md#0x1_capability) - [`0x1::comparator`](comparator.md#0x1_comparator) - [`0x1::copyable_any`](copyable_any.md#0x1_copyable_any) +- [`0x1::crypto_algebra`](crypto_algebra.md#0x1_crypto_algebra) - [`0x1::debug`](debug.md#0x1_debug) - [`0x1::ed25519`](ed25519.md#0x1_ed25519) - [`0x1::fixed_point64`](fixed_point64.md#0x1_fixed_point64) diff --git a/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra.move b/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra.move deleted file mode 100644 index c41a054ae9f8f..0000000000000 --- a/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra.move +++ /dev/null @@ -1,72 +0,0 @@ -/// This module provides generic structs/functions for operations of algebraic structures (e.g. fields and groups), -/// which can be used to build generic cryptographic schemes atop. -/// See `algebra_*.move` for currently implemented algebraic structures. -/// -/// Currently supported operations include element serialization/deserialization and addition. -module aptos_std::algebra { - use std::option::{Option, some, none}; - use std::features::cryptography_algebra_enabled; - - /// This struct represents an element of an algebraic structure `S`. - struct Element has copy, drop { - handle: u64 - } - - // - // Public functions begin. - // - - /// Compute `x + y` for elements `x` and `y` of an algebraic structure `S`. - public fun add(x: &Element, y: &Element): Element { - abort_unless_cryptography_algebra_natives_enabled(); - Element { - handle: add_internal(x.handle, y.handle) - } - } - - /// Try deserializing a byte array to an element of an algebraic structure `S` using a given serialization format `F`. - /// Return none if the deserialization failed. - public fun deserialize(bytes: &vector): Option> { - abort_unless_cryptography_algebra_natives_enabled(); - let (succeeded, handle) = deserialize_internal(bytes); - if (succeeded) { - some(Element { handle }) - } else { - none() - } - } - - /// Serialize an element of an algebraic structure `S` to a byte array using a given serialization format `F`. - public fun serialize(element: &Element): vector { - abort_unless_cryptography_algebra_natives_enabled(); - serialize_internal(element.handle) - } - - // - // (Public functions end here.) - // Native functions begin. - // - - native fun deserialize_internal(bytes: &vector): (bool, u64); - native fun add_internal(handle_1: u64, handle_2: u64): u64; - native fun serialize_internal(handle: u64): vector; - - // - // (Native functions end here.) - // Private functions begin. - // - - fun abort_unless_cryptography_algebra_natives_enabled() { - if (cryptography_algebra_enabled()) return; - abort(std::error::not_implemented(0)) - } - - #[test_only] - public fun enable_cryptography_algebra_natives(fx: &signer) { - std::features::change_feature_flags(fx, vector[std::features::get_cryptography_algebra_natives_feature()], vector[]); - } - - // - // (Private functions end here.) - // -} diff --git a/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra.spec.move b/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra.spec.move deleted file mode 100644 index 6626fa0c1b27c..0000000000000 --- a/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra.spec.move +++ /dev/null @@ -1,14 +0,0 @@ -spec aptos_std::algebra { - - spec deserialize_internal(bytes: &vector): (bool, u64) { - pragma opaque; - } - - spec add_internal(handle_1: u64, handle_2: u64): u64 { - pragma opaque; - } - - spec serialize_internal(handle: u64): vector { - pragma opaque; - } -} diff --git a/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra_bls12381.move b/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra_bls12381.move deleted file mode 100644 index 31bffa3b64d4a..0000000000000 --- a/aptos-move/framework/aptos-stdlib/sources/cryptography/algebra_bls12381.move +++ /dev/null @@ -1,47 +0,0 @@ -/// This module defines marker types, constants and test cases for working with BLS12-381 curves -/// using generic API defined in `algebra.move`. -/// -/// Currently supported BLS12-381 structures include field `Fr`. -module aptos_std::algebra_bls12381 { - // - // Marker types and their serialization schemes begin. - // - - /// The finite field $F_r$ that can be used as the scalar fields - /// for the groups $G_1$, $G_2$, $G_t$ in BLS12-381-based pairing. - struct Fr {} - - /// A serialization format for `Fr` elements, - /// where an element is represented by a byte array `b[]` of size 32 with the least significant byte coming first. - /// - /// NOTE: the same scheme is also used in other implementations (e.g., ark-bls12-381-0.4.0, blst-0.3.7). - struct FrFormatLsb {} - - // - // (Marker types and their serialization schemes end here). - // Tests begin. - // - - #[test_only] - use aptos_std::algebra::{deserialize, serialize, add, enable_cryptography_algebra_natives}; - - #[test_only] - const BLS12_381_FR_VAL_0_SERIALIZED_LSB: vector = x"0000000000000000000000000000000000000000000000000000000000000000"; - #[test_only] - const BLS12_381_FR_VAL_1_SERIALIZED_LSB: vector = x"0100000000000000000000000000000000000000000000000000000000000000"; - - #[test(fx = @std)] - fun test_fr(fx: signer) { - enable_cryptography_algebra_natives(&fx); - let val_0 = std::option::extract(&mut deserialize( - &BLS12_381_FR_VAL_0_SERIALIZED_LSB)); - let val_1 = std::option::extract(&mut deserialize( - &BLS12_381_FR_VAL_1_SERIALIZED_LSB)); - let sum = add(&val_0, &val_1); - assert!(BLS12_381_FR_VAL_1_SERIALIZED_LSB == serialize(&sum), 1); - } - - // - // (Tests end here.) - // -} diff --git a/aptos-move/framework/aptos-stdlib/sources/cryptography/bls12381_algebra.move b/aptos-move/framework/aptos-stdlib/sources/cryptography/bls12381_algebra.move new file mode 100644 index 0000000000000..75d4007dbc25d --- /dev/null +++ b/aptos-move/framework/aptos-stdlib/sources/cryptography/bls12381_algebra.move @@ -0,0 +1,776 @@ +/// This module defines marker types, constants and test cases for working with BLS12-381 curves +/// using the generic API defined in `algebra.move`. +/// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11#name-bls-curves-for-the-128-bit- +/// for the full specification of BLS12-381 curves. +/// +/// Currently-supported BLS12-381 structures include `Fq12`, `Fr`, `G1`, `G2` and `Gt`, +/// along with their widely-used serialization formats, +/// the pairing between `G1`, `G2` and `Gt`, +/// and the hash-to-curve operations for `G1` and `G2` defined in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16. +/// +/// Other unimplemented BLS12-381 structures and serialization formats are also listed here, +/// as they help define some of the currently supported structures. +/// Their implementation may also be added in the future. +/// +/// `Fq`: the finite field $F_q$ used in BLS12-381 curves with a prime order $q$ equal to +/// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab. +/// +/// `FormatFqLsb`: a serialization format for `Fq` elements, +/// where an element is represented by a byte array `b[]` of size 48 with the least significant byte (LSB) coming first. +/// +/// `FormatFqMsb`: a serialization format for `Fq` elements, +/// where an element is represented by a byte array `b[]` of size 48 with the most significant byte (MSB) coming first. +/// +/// `Fq2`: the finite field $F_{q^2}$ used in BLS12-381 curves, +/// which is an extension field of `Fq`, constructed as $F_{q^2}=F_q[u]/(u^2+1)$. +/// +/// `FormatFq2LscLsb`: a serialization format for `Fq2` elements, +/// where an element in the form $(c_0+c_1\cdot u)$ is represented by a byte array `b[]` of size 96, +/// which is a concatenation of its coefficients serialized, with the least significant coefficient (LSC) coming first: +/// - `b[0..48]` is $c_0$ serialized using `FormatFqLsb`. +/// - `b[48..96]` is $c_1$ serialized using `FormatFqLsb`. +/// +/// `FormatFq2MscMsb`: a serialization format for `Fq2` elements, +/// where an element in the form $(c_0+c_1\cdot u)$ is represented by a byte array `b[]` of size 96, +/// which is a concatenation of its coefficients serialized, with the most significant coefficient (MSC) coming first: +/// - `b[0..48]` is $c_1$ serialized using `FormatFqLsb`. +/// - `b[48..96]` is $c_0$ serialized using `FormatFqLsb`. +/// +/// `Fq6`: the finite field $F_{q^6}$ used in BLS12-381 curves, +/// which is an extension field of `Fq2`, constructed as $F_{q^6}=F_{q^2}[v]/(v^3-u-1)$. +/// +/// `FormatFq6LscLsb`: a serialization scheme for `Fq6` elements, +/// where an element in the form $(c_0+c_1\cdot v+c_2\cdot v^2)$ is represented by a byte array `b[]` of size 288, +/// which is a concatenation of its coefficients serialized, with the least significant coefficient (LSC) coming first: +/// - `b[0..96]` is $c_0$ serialized using `FormatFq2LscLsb`. +/// - `b[96..192]` is $c_1$ serialized using `FormatFq2LscLsb`. +/// - `b[192..288]` is $c_2$ serialized using `FormatFq2LscLsb`. +/// +/// `G1Full`: a group constructed by the points on the BLS12-381 curve $E(F_q): y^2=x^3+4$ and the point at infinity, +/// under the elliptic curve point addition. +/// It contains the prime-order subgroup $G_1$ used in pairing. +/// +/// `G2Full`: a group constructed by the points on a curve $E'(F_{q^2}): y^2=x^3+4(u+1)$ and the point at infinity, +/// under the elliptic curve point addition. +/// It contains the prime-order subgroup $G_2$ used in pairing. +module aptos_std::bls12381_algebra { + // + // Marker types + serialization formats begin. + // + + /// The finite field $F_{q^12}$ used in BLS12-381 curves, + /// which is an extension field of `Fq6` (defined in the module documentation), constructed as $F_{q^12}=F_{q^6}[w]/(w^2-v)$. + struct Fq12 {} + + /// A serialization scheme for `Fq12` elements, + /// where an element $(c_0+c_1\cdot w)$ is represented by a byte array `b[]` of size 576, + /// which is a concatenation of its coefficients serialized, with the least significant coefficient (LSC) coming first. + /// - `b[0..288]` is $c_0$ serialized using `FormatFq6LscLsb` (defined in the module documentation). + /// - `b[288..576]` is $c_1$ serialized using `FormatFq6LscLsb`. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + struct FormatFq12LscLsb {} + + /// The group $G_1$ in BLS12-381-based pairing $G_1 \times G_2 \rightarrow G_t$. + /// It is a subgroup of `G1Full` (defined in the module documentation) with a prime order $r$ + /// equal to 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. + /// (so `Fr` is the associated scalar field). + struct G1 {} + + /// A serialization scheme for `G1` elements derived from + /// https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + /// + /// Below is the serialization procedure that takes a `G1` element `p` and outputs a byte array of size 96. + /// 1. Let `(x,y)` be the coordinates of `p` if `p` is on the curve, or `(0,0)` otherwise. + /// 1. Serialize `x` and `y` into `b_x[]` and `b_y[]` respectively using `FormatFqMsb` (defined in the module documentation). + /// 1. Concatenate `b_x[]` and `b_y[]` into `b[]`. + /// 1. If `p` is the point at infinity, set the infinity bit: `b[0]: = b[0] | 0x40`. + /// 1. Return `b[]`. + /// + /// Below is the deserialization procedure that takes a byte array `b[]` and outputs either a `G1` element or none. + /// 1. If the size of `b[]` is not 96, return none. + /// 1. Compute the compression flag as `b[0] & 0x80 != 0`. + /// 1. If the compression flag is true, return none. + /// 1. Compute the infinity flag as `b[0] & 0x40 != 0`. + /// 1. If the infinity flag is set, return the point at infinity. + /// 1. Deserialize `[b[0] & 0x1f, b[1], ..., b[47]]` to `x` using `FormatFqMsb`. If `x` is none, return none. + /// 1. Deserialize `[b[48], ..., b[95]]` to `y` using `FormatFqMsb`. If `y` is none, return none. + /// 1. Check if `(x,y)` is on curve `E`. If not, return none. + /// 1. Check if `(x,y)` is in the subgroup of order `r`. If not, return none. + /// 1. Return `(x,y)`. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + struct FormatG1Uncompr {} + + /// A serialization scheme for `G1` elements derived from + /// https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + /// + /// Below is the serialization procedure that takes a `G1` element `p` and outputs a byte array of size 48. + /// 1. Let `(x,y)` be the coordinates of `p` if `p` is on the curve, or `(0,0)` otherwise. + /// 1. Serialize `x` into `b[]` using `FormatFqMsb` (defined in the module documentation). + /// 1. Set the compression bit: `b[0] := b[0] | 0x80`. + /// 1. If `p` is the point at infinity, set the infinity bit: `b[0]: = b[0] | 0x40`. + /// 1. If `y > -y`, set the lexicographical flag: `b[0] := b[0] | 0x20`. + /// 1. Return `b[]`. + /// + /// Below is the deserialization procedure that takes a byte array `b[]` and outputs either a `G1` element or none. + /// 1. If the size of `b[]` is not 48, return none. + /// 1. Compute the compression flag as `b[0] & 0x80 != 0`. + /// 1. If the compression flag is false, return none. + /// 1. Compute the infinity flag as `b[0] & 0x40 != 0`. + /// 1. If the infinity flag is set, return the point at infinity. + /// 1. Compute the lexicographical flag as `b[0] & 0x20 != 0`. + /// 1. Deserialize `[b[0] & 0x1f, b[1], ..., b[47]]` to `x` using `FormatFqMsb`. If `x` is none, return none. + /// 1. Solve the curve equation with `x` for `y`. If no such `y` exists, return none. + /// 1. Let `y'` be `max(y,-y)` if the lexicographical flag is set, or `min(y,-y)` otherwise. + /// 1. Check if `(x,y')` is in the subgroup of order `r`. If not, return none. + /// 1. Return `(x,y')`. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + struct FormatG1Compr {} + + /// The group $G_2$ in BLS12-381-based pairing $G_1 \times G_2 \rightarrow G_t$. + /// It is a subgroup of `G2Full` (defined in the module documentation) with a prime order $r$ equal to + /// 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. + /// (so `Fr` is the scalar field). + struct G2 {} + + /// A serialization scheme for `G2` elements derived from + /// https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + /// + /// Below is the serialization procedure that takes a `G2` element `p` and outputs a byte array of size 192. + /// 1. Let `(x,y)` be the coordinates of `p` if `p` is on the curve, or `(0,0)` otherwise. + /// 1. Serialize `x` and `y` into `b_x[]` and `b_y[]` respectively using `FormatFq2MscMsb` (defined in the module documentation). + /// 1. Concatenate `b_x[]` and `b_y[]` into `b[]`. + /// 1. If `p` is the point at infinity, set the infinity bit in `b[]`: `b[0]: = b[0] | 0x40`. + /// 1. Return `b[]`. + /// + /// Below is the deserialization procedure that takes a byte array `b[]` and outputs either a `G2` element or none. + /// 1. If the size of `b[]` is not 192, return none. + /// 1. Compute the compression flag as `b[0] & 0x80 != 0`. + /// 1. If the compression flag is true, return none. + /// 1. Compute the infinity flag as `b[0] & 0x40 != 0`. + /// 1. If the infinity flag is set, return the point at infinity. + /// 1. Deserialize `[b[0] & 0x1f, ..., b[95]]` to `x` using `FormatFq2MscMsb`. If `x` is none, return none. + /// 1. Deserialize `[b[96], ..., b[191]]` to `y` using `FormatFq2MscMsb`. If `y` is none, return none. + /// 1. Check if `(x,y)` is on the curve `E'`. If not, return none. + /// 1. Check if `(x,y)` is in the subgroup of order `r`. If not, return none. + /// 1. Return `(x,y)`. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + struct FormatG2Uncompr {} + + /// A serialization scheme for `G2` elements derived from + /// https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-11.html#name-zcash-serialization-format-. + /// + /// Below is the serialization procedure that takes a `G2` element `p` and outputs a byte array of size 96. + /// 1. Let `(x,y)` be the coordinates of `p` if `p` is on the curve, or `(0,0)` otherwise. + /// 1. Serialize `x` into `b[]` using `FormatFq2MscMsb` (defined in the module documentation). + /// 1. Set the compression bit: `b[0] := b[0] | 0x80`. + /// 1. If `p` is the point at infinity, set the infinity bit: `b[0]: = b[0] | 0x40`. + /// 1. If `y > -y`, set the lexicographical flag: `b[0] := b[0] | 0x20`. + /// 1. Return `b[]`. + /// + /// Below is the deserialization procedure that takes a byte array `b[]` and outputs either a `G2` element or none. + /// 1. If the size of `b[]` is not 96, return none. + /// 1. Compute the compression flag as `b[0] & 0x80 != 0`. + /// 1. If the compression flag is false, return none. + /// 1. Compute the infinity flag as `b[0] & 0x40 != 0`. + /// 1. If the infinity flag is set, return the point at infinity. + /// 1. Compute the lexicographical flag as `b[0] & 0x20 != 0`. + /// 1. Deserialize `[b[0] & 0x1f, b[1], ..., b[95]]` to `x` using `FormatFq2MscMsb`. If `x` is none, return none. + /// 1. Solve the curve equation with `x` for `y`. If no such `y` exists, return none. + /// 1. Let `y'` be `max(y,-y)` if the lexicographical flag is set, or `min(y,-y)` otherwise. + /// 1. Check if `(x,y')` is in the subgroup of order `r`. If not, return none. + /// 1. Return `(x,y')`. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + struct FormatG2Compr {} + + /// The group $G_t$ in BLS12-381-based pairing $G_1 \times G_2 \rightarrow G_t$. + /// It is a multiplicative subgroup of `Fq12`, + /// with a prime order $r$ equal to 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. + /// (so `Fr` is the scalar field). + /// The identity of `Gt` is 1. + struct Gt {} + + /// A serialization scheme for `Gt` elements. + /// + /// To serialize, it treats a `Gt` element `p` as an `Fq12` element and serialize it using `FormatFq12LscLsb`. + /// + /// To deserialize, it uses `FormatFq12LscLsb` to try deserializing to an `Fq12` element then test the membership in `Gt`. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0. + struct FormatGt {} + + /// The finite field $F_r$ that can be used as the scalar fields + /// associated with the groups $G_1$, $G_2$, $G_t$ in BLS12-381-based pairing. + struct Fr {} + + /// A serialization format for `Fr` elements, + /// where an element is represented by a byte array `b[]` of size 32 with the least significant byte (LSB) coming first. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0, blst-0.3.7. + struct FormatFrLsb {} + + /// A serialization scheme for `Fr` elements, + /// where an element is represented by a byte array `b[]` of size 32 with the most significant byte (MSB) coming first. + /// + /// NOTE: other implementation(s) using this format: ark-bls12-381-0.4.0, blst-0.3.7. + struct FormatFrMsb {} + + // + // (Marker types + serialization formats end here.) + // Hash-to-structure suites begin. + // + + /// The hash-to-curve suite `BLS12381G1_XMD:SHA-256_SSWU_RO_` that hashes a byte array into `G1` elements. + /// + /// Full specification is defined in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#name-bls12-381-g1. + struct HashG1XmdSha256SswuRo {} + + /// The hash-to-curve suite `BLS12381G2_XMD:SHA-256_SSWU_RO_` that hashes a byte array into `G2` elements. + /// + /// Full specification is defined in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#name-bls12-381-g2. + struct HashG2XmdSha256SswuRo {} + + // + // (Hash-to-structure suites end here.) + // Tests begin. + // + + #[test_only] + const FQ12_VAL_0_SERIALIZED: vector = x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const FQ12_VAL_1_SERIALIZED: vector = x"010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const FQ12_VAL_7_SERIALIZED: vector = x"070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const FQ12_VAL_7_NEG_SERIALIZED: vector = x"a4aafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const Q12_SERIALIZED: vector = x"1175f55da544c7625f8ccb1360e2b1d3ca40747811c8f5ed04440afe232b476c0215676aec05f2a44ac2da6b6d1b7cff075e7b2a587e0aab601a8d3db4f0d29906e5e4d0d78119f396d5a59f0f8d1ca8bca62540be6ab9c12d0ca00de1f311f106278d000e55a393c9766a74e0d08a298450f60d7e666575e3354bf14b8731f4e721c0c180a5ed55c2f8f51f815baecbf96b5fc717eb58ac161a27d1d5f2bdc1a079609b9d6449165b2466b32a01eac7992a1ea0cac2f223cde1d56f9bbccc67afe44621daf858df3fc0eb837818f3e42ab3e131ce4e492efa63c108e6ef91c29ed63b3045baebcb0ab8d203c7f558beaffccba31b12aca7f54b58d0c28340e4fdb3c7c94fe9c4fef9d640ff2fcff02f1748416cbed0981fbff49f0e39eaf8a30273e67ed851944d33d6a593ef5ddcd62da84568822a6045b633bf6a513b3cfe8f9de13e76f8dcbd915980dec205eab6a5c0c72dcebd9afff1d25509ddbf33f8e24131fbd74cda93336514340cf8036b66b09ed9e6a6ac37e22fb3ac407e321beae8cd9fe74c8aaeb4edaa9a7272848fc623f6fe835a2e647379f547fc5ec6371318a85bfa60009cb20ccbb8a467492988a87633c14c0324ba0d0c3e1798ed29c8494cea35023746da05e35d184b4a301d5b2238d665495c6318b5af8653758008952d06cb9e62487b196d64383c73c06d6e1cccdf9b3ce8f95679e7050d949004a55f4ccf95b2552880ae36d1f7e09504d2338316d87d14a064511a295d768113e301bdf9d4383a8be32192d3f2f3b2de14181c73839a7cb4af5301"; + + #[test_only] + fun rand_vector(num: u64): vector> { + let elements = vector[]; + while (num > 0) { + std::vector::push_back(&mut elements, rand_insecure()); + num = num - 1; + }; + elements + } + + #[test(fx = @std)] + fun test_fq12(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Constants. + assert!(Q12_SERIALIZED == order(), 1); + + // Serialization/deserialization. + let val_0 = zero(); + let val_1 = one(); + assert!(FQ12_VAL_0_SERIALIZED == serialize(&val_0), 1); + assert!(FQ12_VAL_1_SERIALIZED == serialize(&val_1), 1); + let val_7 = from_u64(7); + let val_7_another = std::option::extract(&mut deserialize(&FQ12_VAL_7_SERIALIZED)); + assert!(eq(&val_7, &val_7_another), 1); + assert!(FQ12_VAL_7_SERIALIZED == serialize(&val_7), 1); + assert!(std::option::is_none(&deserialize(&x"ffff")), 1); + + // Negation. + let val_minus_7 = neg(&val_7); + assert!(FQ12_VAL_7_NEG_SERIALIZED == serialize(&val_minus_7), 1); + + // Addition. + let val_9 = from_u64(9); + let val_2 = from_u64(2); + assert!(eq(&val_2, &add(&val_minus_7, &val_9)), 1); + + // Subtraction. + assert!(eq(&val_9, &sub(&val_2, &val_minus_7)), 1); + + // Multiplication. + let val_63 = from_u64(63); + assert!(eq(&val_63, &mul(&val_7, &val_9)), 1); + + // division. + let val_0 = from_u64(0); + assert!(eq(&val_7, &std::option::extract(&mut div(&val_63, &val_9))), 1); + assert!(std::option::is_none(&div(&val_63, &val_0)), 1); + + // Inversion. + assert!(eq(&val_minus_7, &neg(&val_7)), 1); + assert!(std::option::is_none(&inv(&val_0)), 1); + + // Squaring. + let val_x = rand_insecure(); + assert!(eq(&mul(&val_x, &val_x), &sqr(&val_x)), 1); + + // Downcasting. + assert!(eq(&zero(), &std::option::extract(&mut downcast(&val_1))), 1); + } + + #[test_only] + const R_SERIALIZED: vector = x"01000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73"; + #[test_only] + const G1_INF_SERIALIZED_COMP: vector = x"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const G1_INF_SERIALIZED_UNCOMP: vector = x"400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const G1_GENERATOR_SERIALIZED_COMP: vector = x"97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"; + #[test_only] + const G1_GENERATOR_SERIALIZED_UNCOMP: vector = x"17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"; + #[test_only] + const G1_GENERATOR_MUL_BY_7_SERIALIZED_COMP: vector = x"b928f3beb93519eecf0145da903b40a4c97dca00b21f12ac0df3be9116ef2ef27b2ae6bcd4c5bc2d54ef5a70627efcb7"; + #[test_only] + const G1_GENERATOR_MUL_BY_7_SERIALIZED_UNCOMP: vector = x"1928f3beb93519eecf0145da903b40a4c97dca00b21f12ac0df3be9116ef2ef27b2ae6bcd4c5bc2d54ef5a70627efcb7108dadbaa4b636445639d5ae3089b3c43a8a1d47818edd1839d7383959a41c10fdc66849cfa1b08c5a11ec7e28981a1c"; + #[test_only] + const G1_GENERATOR_MUL_BY_7_NEG_SERIALIZED_COMP: vector = x"9928f3beb93519eecf0145da903b40a4c97dca00b21f12ac0df3be9116ef2ef27b2ae6bcd4c5bc2d54ef5a70627efcb7"; + #[test_only] + const G1_GENERATOR_MUL_BY_7_NEG_SERIALIZED_UNCOMP: vector = x"1928f3beb93519eecf0145da903b40a4c97dca00b21f12ac0df3be9116ef2ef27b2ae6bcd4c5bc2d54ef5a70627efcb70973642f94c9b055f4e1d20812c1f91329ed2e3d71f635a72d599a679d0cda1320e597b4e1b24f735fed1381d767908f"; + + #[test(fx = @std)] + fun test_g1affine(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Constants. + assert!(R_SERIALIZED == order(), 1); + let point_at_infinity = zero(); + let generator = one(); + + // Serialization/deserialization. + assert!(G1_GENERATOR_SERIALIZED_UNCOMP == serialize(&generator), 1); + assert!(G1_GENERATOR_SERIALIZED_COMP == serialize(&generator), 1); + let generator_from_comp = std::option::extract(&mut deserialize(&G1_GENERATOR_SERIALIZED_COMP + )); + let generator_from_uncomp = std::option::extract(&mut deserialize(&G1_GENERATOR_SERIALIZED_UNCOMP + )); + assert!(eq(&generator, &generator_from_comp), 1); + assert!(eq(&generator, &generator_from_uncomp), 1); + + // Deserialization should fail if given a byte array of correct size but the value is not a member. + assert!(std::option::is_none(&deserialize(&x"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")), 1); + + // Deserialization should fail if given a byte array of wrong size. + assert!(std::option::is_none(&deserialize(&x"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")), 1); + + assert!( + G1_INF_SERIALIZED_UNCOMP == serialize(&point_at_infinity), 1); + assert!(G1_INF_SERIALIZED_COMP == serialize(&point_at_infinity), 1); + let inf_from_uncomp = std::option::extract(&mut deserialize(&G1_INF_SERIALIZED_UNCOMP + )); + let inf_from_comp = std::option::extract(&mut deserialize(&G1_INF_SERIALIZED_COMP + )); + assert!(eq(&point_at_infinity, &inf_from_comp), 1); + assert!(eq(&point_at_infinity, &inf_from_uncomp), 1); + + let point_7g_from_uncomp = std::option::extract(&mut deserialize(&G1_GENERATOR_MUL_BY_7_SERIALIZED_UNCOMP + )); + let point_7g_from_comp = std::option::extract(&mut deserialize(&G1_GENERATOR_MUL_BY_7_SERIALIZED_COMP + )); + assert!(eq(&point_7g_from_comp, &point_7g_from_uncomp), 1); + + // Deserialization should fail if given a point on the curve but off its prime-order subgroup, e.g., `(0,2)`. + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002")), 1); + assert!(std::option::is_none(&deserialize(&x"800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), 1); + + // Deserialization should fail if given a valid point in (Fq,Fq) but not on the curve. + assert!(std::option::is_none(&deserialize(&x"8959e137e0719bf872abb08411010f437a8955bd42f5ba20fca64361af58ce188b1adb96ef229698bb7860b79e24ba12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), 1); + + // Deserialization should fail if given an invalid point (x not in Fq). + assert!(std::option::is_none(&deserialize(&x"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa76e9853b35f5c9b2002d9e5833fd8f9ab4cd3934a4722a06f6055bfca720c91629811e2ecae7f0cf301b6d07898a90f")), 1); + assert!(std::option::is_none(&deserialize(&x"9fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")), 1); + + // Deserialization should fail if given a byte array of wrong size. + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab")), 1); + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab")), 1); + + // Scalar multiplication. + let scalar_7 = from_u64(7); + let point_7g_calc = scalar_mul(&generator, &scalar_7); + assert!(eq(&point_7g_calc, &point_7g_from_comp), 1); + assert!(G1_GENERATOR_MUL_BY_7_SERIALIZED_UNCOMP == serialize(&point_7g_calc), 1); + assert!(G1_GENERATOR_MUL_BY_7_SERIALIZED_COMP == serialize( &point_7g_calc), 1); + + // Multi-scalar multiplication. + let num_entries = 1; + while (num_entries < 10) { + let scalars = rand_vector(num_entries); + let elements = rand_vector(num_entries); + + let expected = zero(); + let i = 0; + while (i < num_entries) { + let element = std::vector::borrow(&elements, i); + let scalar = std::vector::borrow(&scalars, i); + expected = add(&expected, &scalar_mul(element, scalar)); + i = i + 1; + }; + + let actual = multi_scalar_mul(&elements, &scalars); + assert!(eq(&expected, &actual), 1); + + num_entries = num_entries + 1; + }; + + // Doubling. + let scalar_2 = from_u64(2); + let point_2g = scalar_mul(&generator, &scalar_2); + let point_double_g = double(&generator); + assert!(eq(&point_2g, &point_double_g), 1); + + // Negation. + let point_minus_7g_calc = neg(&point_7g_calc); + assert!(G1_GENERATOR_MUL_BY_7_NEG_SERIALIZED_COMP == serialize(&point_minus_7g_calc), 1); + assert!(G1_GENERATOR_MUL_BY_7_NEG_SERIALIZED_UNCOMP == serialize(&point_minus_7g_calc), 1); + + // Addition. + let scalar_9 = from_u64(9); + let point_9g = scalar_mul(&generator, &scalar_9); + let point_2g = scalar_mul(&generator, &scalar_2); + let point_2g_calc = add(&point_minus_7g_calc, &point_9g); + assert!(eq(&point_2g, &point_2g_calc), 1); + + // Subtraction. + assert!(eq(&point_9g, &sub(&point_2g, &point_minus_7g_calc)), 1); + + // Hash-to-group using suite `BLS12381G1_XMD:SHA-256_SSWU_RO_`. + // Test vectors source: https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-bls12381g1_xmdsha-256_sswu_ + let actual = hash_to(&b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_", &b""); + let expected = std::option::extract(&mut deserialize(&x"052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a108ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265")); + assert!(eq(&expected, &actual), 1); + let actual = hash_to(&b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_", &b"abcdef0123456789"); + let expected = std::option::extract(&mut deserialize(&x"11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d9803a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709")); + assert!(eq(&expected, &actual), 1); + } + + #[test_only] + const G2_INF_SERIALIZED_UNCOMP: vector = x"400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const G2_INF_SERIALIZED_COMP: vector = x"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const G2_GENERATOR_SERIALIZED_UNCOMP: vector = x"13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801"; + #[test_only] + const G2_GENERATOR_SERIALIZED_COMP: vector = x"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"; + #[test_only] + const G2_GENERATOR_MUL_BY_7_SERIALIZED_UNCOMP: vector = x"0d0273f6bf31ed37c3b8d68083ec3d8e20b5f2cc170fa24b9b5be35b34ed013f9a921f1cad1644d4bdb14674247234c8049cd1dbb2d2c3581e54c088135fef36505a6823d61b859437bfc79b617030dc8b40e32bad1fa85b9c0f368af6d38d3c05ecf93654b7a1885695aaeeb7caf41b0239dc45e1022be55d37111af2aecef87799638bec572de86a7437898efa702008b7ae4dbf802c17a6648842922c9467e460a71c88d393ee7af356da123a2f3619e80c3bdcc8e2b1da52f8cd9913ccdd"; + #[test_only] + const G2_GENERATOR_MUL_BY_7_SERIALIZED_COMP: vector = x"8d0273f6bf31ed37c3b8d68083ec3d8e20b5f2cc170fa24b9b5be35b34ed013f9a921f1cad1644d4bdb14674247234c8049cd1dbb2d2c3581e54c088135fef36505a6823d61b859437bfc79b617030dc8b40e32bad1fa85b9c0f368af6d38d3c"; + #[test_only] + const G2_GENERATOR_MUL_BY_7_NEG_SERIALIZED_UNCOMP: vector = x"0d0273f6bf31ed37c3b8d68083ec3d8e20b5f2cc170fa24b9b5be35b34ed013f9a921f1cad1644d4bdb14674247234c8049cd1dbb2d2c3581e54c088135fef36505a6823d61b859437bfc79b617030dc8b40e32bad1fa85b9c0f368af6d38d3c141418b3e4c84511f485fcc78b80b8bc623d6f3f1282e6da09f9c1860402272ba7129c72c4fcd2174f8ac87671053a8b1149639c79ffba82a4b71f73b11f186f8016a4686ab17ed0ec3d7bc6e476c6ee04c3f3c2d48b1d4ddfac073266ebddce"; + #[test_only] + const G2_GENERATOR_MUL_BY_7_NEG_SERIALIZED_COMP: vector = x"ad0273f6bf31ed37c3b8d68083ec3d8e20b5f2cc170fa24b9b5be35b34ed013f9a921f1cad1644d4bdb14674247234c8049cd1dbb2d2c3581e54c088135fef36505a6823d61b859437bfc79b617030dc8b40e32bad1fa85b9c0f368af6d38d3c"; + + #[test(fx = @std)] + fun test_g2affine(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Special constants. + assert!(R_SERIALIZED == order(), 1); + let point_at_infinity = zero(); + let generator = one(); + + // Serialization/deserialization. + assert!(G2_GENERATOR_SERIALIZED_COMP == serialize(&generator), 1); + assert!(G2_GENERATOR_SERIALIZED_UNCOMP == serialize(&generator), 1); + let generator_from_uncomp = std::option::extract(&mut deserialize(&G2_GENERATOR_SERIALIZED_UNCOMP + )); + let generator_from_comp = std::option::extract(&mut deserialize(&G2_GENERATOR_SERIALIZED_COMP + )); + assert!(eq(&generator, &generator_from_comp), 1); + assert!(eq(&generator, &generator_from_uncomp), 1); + assert!(G2_INF_SERIALIZED_UNCOMP == serialize(&point_at_infinity), 1); + assert!(G2_INF_SERIALIZED_COMP == serialize(&point_at_infinity), 1); + let inf_from_uncomp = std::option::extract(&mut deserialize(&G2_INF_SERIALIZED_UNCOMP)); + let inf_from_comp = std::option::extract(&mut deserialize(&G2_INF_SERIALIZED_COMP)); + assert!(eq(&point_at_infinity, &inf_from_comp), 1); + assert!(eq(&point_at_infinity, &inf_from_uncomp), 1); + let point_7g_from_uncomp = std::option::extract(&mut deserialize(&G2_GENERATOR_MUL_BY_7_SERIALIZED_UNCOMP + )); + let point_7g_from_comp = std::option::extract(&mut deserialize(&G2_GENERATOR_MUL_BY_7_SERIALIZED_COMP + )); + assert!(eq(&point_7g_from_comp, &point_7g_from_uncomp), 1); + + // Deserialization should fail if given a point on the curve but not in the prime-order subgroup. + assert!(std::option::is_none(&deserialize(&x"f037d4ccd5ee751eba1c1fd4c7edbb76d2b04c3a1f3f554827cf37c3acbc2dbb7cdb320a2727c2462d6c55ca1f637707b96eeebc622c1dbe7c56c34f93887c8751b42bd04f29253a82251c192ef27ece373993b663f4360505299c5bd18c890ddd862a6308796bf47e2265073c1f7d81afd69f9497fc1403e2e97a866129b43b672295229c21116d4a99f3e5c2ae720a31f181dbed8a93e15f909c20cf69d11a8879adbbe6890740def19814e6d4ed23fb0dcbd79291655caf48b466ac9cae04")), 1); + assert!(std::option::is_none(&deserialize(&x"f037d4ccd5ee751eba1c1fd4c7edbb76d2b04c3a1f3f554827cf37c3acbc2dbb7cdb320a2727c2462d6c55ca1f637707b96eeebc622c1dbe7c56c34f93887c8751b42bd04f29253a82251c192ef27ece373993b663f4360505299c5bd18c890d")), 1); + + // Deserialization should fail if given a valid point in (Fq2,Fq2) but not on the curve. + assert!(std::option::is_none(&deserialize(&x"f037d4ccd5ee751eba1c1fd4c7edbb76d2b04c3a1f3f554827cf37c3acbc2dbb7cdb320a2727c2462d6c55ca1f637707b96eeebc622c1dbe7c56c34f93887c8751b42bd04f29253a82251c192ef27ece373993b663f4360505299c5bd18c890d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), 1); + + // Deserialization should fail if given an invalid point (x not in Fq2). + assert!(std::option::is_none(&deserialize(&x"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdd862a6308796bf47e2265073c1f7d81afd69f9497fc1403e2e97a866129b43b672295229c21116d4a99f3e5c2ae720a31f181dbed8a93e15f909c20cf69d11a8879adbbe6890740def19814e6d4ed23fb0dcbd79291655caf48b466ac9cae04")), 1); + assert!(std::option::is_none(&deserialize(&x"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")), 1); + + // Deserialization should fail if given a byte array of wrong size. + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab")), 1); + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab")), 1); + + // Scalar multiplication. + let scalar_7 = from_u64(7); + let point_7g_calc = scalar_mul(&generator, &scalar_7); + assert!(eq(&point_7g_calc, &point_7g_from_comp), 1); + assert!(G2_GENERATOR_MUL_BY_7_SERIALIZED_UNCOMP == serialize(&point_7g_calc), 1); + assert!(G2_GENERATOR_MUL_BY_7_SERIALIZED_COMP == serialize(&point_7g_calc), 1); + + // Multi-scalar multiplication. + let num_entries = 1; + while (num_entries < 10) { + let scalars = rand_vector(num_entries); + let elements = rand_vector(num_entries); + + let expected = zero(); + let i = 0; + while (i < num_entries) { + let element = std::vector::borrow(&elements, i); + let scalar = std::vector::borrow(&scalars, i); + expected = add(&expected, &scalar_mul(element, scalar)); + i = i + 1; + }; + + let actual = multi_scalar_mul(&elements, &scalars); + assert!(eq(&expected, &actual), 1); + + num_entries = num_entries + 1; + }; + + // Doubling. + let scalar_2 = from_u64(2); + let point_2g = scalar_mul(&generator, &scalar_2); + let point_double_g = double(&generator); + assert!(eq(&point_2g, &point_double_g), 1); + + // Negation. + let point_minus_7g_calc = neg(&point_7g_calc); + assert!(G2_GENERATOR_MUL_BY_7_NEG_SERIALIZED_COMP == serialize(&point_minus_7g_calc), 1); + assert!(G2_GENERATOR_MUL_BY_7_NEG_SERIALIZED_UNCOMP == serialize(&point_minus_7g_calc), 1); + + // Addition. + let scalar_9 = from_u64(9); + let point_9g = scalar_mul(&generator, &scalar_9); + let point_2g = scalar_mul(&generator, &scalar_2); + let point_2g_calc = add(&point_minus_7g_calc, &point_9g); + assert!(eq(&point_2g, &point_2g_calc), 1); + + // Subtraction. + assert!(eq(&point_9g, &sub(&point_2g, &point_minus_7g_calc)), 1); + + // Hash-to-group using suite `BLS12381G2_XMD:SHA-256_SSWU_RO_`. + // Test vectors source: https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-bls12381g2_xmdsha-256_sswu_ + let actual = hash_to(&b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_", &b""); + let expected = std::option::extract(&mut deserialize(&x"05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d60503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92")); + assert!(eq(&expected, &actual), 1); + let actual = hash_to(&b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_", &b"abcdef0123456789"); + let expected = std::option::extract(&mut deserialize(&x"190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd00bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8")); + assert!(eq(&expected, &actual), 1); + } + + #[test_only] + const FQ12_ONE_SERIALIZED: vector = x"010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const GT_GENERATOR_SERIALIZED: vector = x"b68917caaa0543a808c53908f694d1b6e7b38de90ce9d83d505ca1ef1b442d2727d7d06831d8b2a7920afc71d8eb50120f17a0ea982a88591d9f43503e94a8f1abaf2e4589f65aafb7923c484540a868883432a5c60e75860b11e5465b1c9a08873ec29e844c1c888cb396933057ffdd541b03a5220eda16b2b3a6728ea678034ce39c6839f20397202d7c5c44bb68134f93193cec215031b17399577a1de5ff1f5b0666bdd8907c61a7651e4e79e0372951505a07fa73c25788db6eb8023519a5aa97b51f1cad1d43d8aabbff4dc319c79a58cafc035218747c2f75daf8f2fb7c00c44da85b129113173d4722f5b201b6b4454062e9ea8ba78c5ca3cadaf7238b47bace5ce561804ae16b8f4b63da4645b8457a93793cbd64a7254f150781019de87ee42682940f3e70a88683d512bb2c3fb7b2434da5dedbb2d0b3fb8487c84da0d5c315bdd69c46fb05d23763f2191aabd5d5c2e12a10b8f002ff681bfd1b2ee0bf619d80d2a795eb22f2aa7b85d5ffb671a70c94809f0dafc5b73ea2fb0657bae23373b4931bc9fa321e8848ef78894e987bff150d7d671aee30b3931ac8c50e0b3b0868effc38bf48cd24b4b811a2995ac2a09122bed9fd9fa0c510a87b10290836ad06c8203397b56a78e9a0c61c77e56ccb4f1bc3d3fcaea7550f3503efe30f2d24f00891cb45620605fcfaa4292687b3a7db7c1c0554a93579e889a121fd8f72649b2402996a084d2381c5043166673b3849e4fd1e7ee4af24aa8ed443f56dfd6b68ffde4435a92cd7a4ac3bc77e1ad0cb728606cf08bf6386e5410f"; + #[test_only] + const GT_GENERATOR_MUL_BY_7_SERIALIZED: vector = x"2041ea7b66c19680e2c0bb23245a71918753220b31f88a925aa9b1e192e7c188a0b365cb994b3ec5e809206117c6411242b940b10caa37ce734496b3b7c63578a0e3c076f9b31a7ca13a716262e0e4cda4ac994efb9e19893cbfe4d464b9210d099d808a08b3c4c3846e7529984899478639c4e6c46152ef49a04af9c8e6ff442d286c4613a3dac6a4bee4b40e1f6b030f2871dabe4223b250c3181ecd3bc6819004745aeb6bac567407f2b9c7d1978c45ee6712ae46930bc00638383f6696158bad488cbe7663d681c96c035481dbcf78e7a7fbaec3799163aa6914cef3365156bdc3e533a7c883d5974e3462ac6f19e3f9ce26800ae248a45c5f0dd3a48a185969224e6cd6af9a048241bdcac9800d94aeee970e08488fb961e36a769b6c185d185b4605dc9808517196bba9d00a3e37bca466c19187486db104ee03962d39fe473e276355618e44c965f05082bb027a7baa4bcc6d8c0775c1e8a481e77df36ddad91e75a982302937f543a11fe71922dcd4f46fe8f951f91cde412b359507f2b3b6df0374bfe55c9a126ad31ce254e67d64194d32d7955ec791c9555ea5a917fc47aba319e909de82da946eb36e12aff936708402228295db2712f2fc807c95092a86afd71220699df13e2d2fdf2857976cb1e605f72f1b2edabadba3ff05501221fe81333c13917c85d725ce92791e115eb0289a5d0b3330901bb8b0ed146abeb81381b7331f1c508fb14e057b05d8b0190a9e74a3d046dcd24e7ab747049945b3d8a120c4f6d88e67661b55573aa9b361367488a1ef7dffd967d64a1518"; + #[test_only] + const GT_GENERATOR_MUL_BY_7_NEG_SERIALIZED: vector = x"2041ea7b66c19680e2c0bb23245a71918753220b31f88a925aa9b1e192e7c188a0b365cb994b3ec5e809206117c6411242b940b10caa37ce734496b3b7c63578a0e3c076f9b31a7ca13a716262e0e4cda4ac994efb9e19893cbfe4d464b9210d099d808a08b3c4c3846e7529984899478639c4e6c46152ef49a04af9c8e6ff442d286c4613a3dac6a4bee4b40e1f6b030f2871dabe4223b250c3181ecd3bc6819004745aeb6bac567407f2b9c7d1978c45ee6712ae46930bc00638383f6696158bad488cbe7663d681c96c035481dbcf78e7a7fbaec3799163aa6914cef3365156bdc3e533a7c883d5974e3462ac6f19e3f9ce26800ae248a45c5f0dd3a48a185969224e6cd6af9a048241bdcac9800d94aeee970e08488fb961e36a769b6c184e92a4b9fa2366b1ae8ebdf5542fa1e0ec390c90df40a91e5261800581b5492bd9640d1c5352babc551d1a49998f4517312f55b4339272b28a3e6b0c7d182e2bb61bd7d72b29ae3696db8fafe32b904ab5d0764e46bf21f9a0c9a1f7bedc6b12b9f64820fc8b3fd4a26541472be3c9c93d784cdd53a059d1604bf3292fedd1babfb00398128e3241bc63a5a47b5e9207fcb0c88f7bfddc376a242c9f0c032ba28eec8670f1fa1d47567593b4571c983b8015df91cfa1241b7fb8a57e0e6e01145b98de017eccc2a66e83ced9d83119a505e552467838d35b8ce2f4d7cc9a894f6dee922f35f0e72b7e96f0879b0c8614d3f9e5f5618b5be9b82381628448641a8bb0fd1dffb16c70e6831d8d69f61f2a2ef9e90c421f7a5b1ce7a5d113c7eb01"; + + #[test(fx = @std)] + fun test_gt(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Special constants. + assert!(R_SERIALIZED == order(), 1); + let identity = zero(); + let generator = one(); + + // Serialization/deserialization. + assert!(GT_GENERATOR_SERIALIZED == serialize(&generator), 1); + let generator_from_deser = std::option::extract(&mut deserialize(>_GENERATOR_SERIALIZED)); + assert!(eq(&generator, &generator_from_deser), 1); + assert!(FQ12_ONE_SERIALIZED == serialize(&identity), 1); + let identity_from_deser = std::option::extract(&mut deserialize(&FQ12_ONE_SERIALIZED)); + assert!(eq(&identity, &identity_from_deser), 1); + let element_7g_from_deser = std::option::extract(&mut deserialize(>_GENERATOR_MUL_BY_7_SERIALIZED + )); + assert!(std::option::is_none(&deserialize(&x"ffff")), 1); + + // Deserialization should fail if given an element in Fq12 but not in the prime-order subgroup. + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")), 1); + + // Deserialization should fail if given a byte array of wrong size. + assert!(std::option::is_none(&deserialize(&x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab")), 1); + + // Element scalar multiplication. + let scalar_7 = from_u64(7); + let element_7g_calc = scalar_mul(&generator, &scalar_7); + assert!(eq(&element_7g_calc, &element_7g_from_deser), 1); + assert!(GT_GENERATOR_MUL_BY_7_SERIALIZED == serialize(&element_7g_calc), 1); + + // Element negation. + let element_minus_7g_calc = neg(&element_7g_calc); + assert!(GT_GENERATOR_MUL_BY_7_NEG_SERIALIZED == serialize(&element_minus_7g_calc), 1); + + // Element addition. + let scalar_9 = from_u64(9); + let element_9g = scalar_mul(&generator, &scalar_9); + let scalar_2 = from_u64(2); + let element_2g = scalar_mul(&generator, &scalar_2); + let element_2g_calc = add(&element_minus_7g_calc, &element_9g); + assert!(eq(&element_2g, &element_2g_calc), 1); + + // Subtraction. + assert!(eq(&element_9g, &sub(&element_2g, &element_minus_7g_calc)), 1); + + // Upcasting to Fq12. + assert!(eq(&one(), &upcast(&identity)), 1); + } + + #[test_only] + use aptos_std::crypto_algebra::{zero, one, from_u64, eq, deserialize, serialize, neg, add, sub, mul, div, inv, rand_insecure, sqr, order, scalar_mul, multi_scalar_mul, double, hash_to, upcast, enable_cryptography_algebra_natives, pairing, multi_pairing, downcast, Element}; + + #[test_only] + const FR_VAL_0_SERIALIZED_LSB: vector = x"0000000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const FR_VAL_1_SERIALIZED_LSB: vector = x"0100000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const FR_VAL_7_SERIALIZED_LSB: vector = x"0700000000000000000000000000000000000000000000000000000000000000"; + #[test_only] + const FR_VAL_7_SERIALIZED_MSB: vector = x"0000000000000000000000000000000000000000000000000000000000000007"; + #[test_only] + const FR_VAL_7_NEG_SERIALIZED_LSB: vector = x"fafffffffefffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73"; + + #[test(fx = @std)] + fun test_fr(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Constants. + assert!(R_SERIALIZED == order(), 1); + + // Serialization/deserialization. + let val_0 = zero(); + let val_1 = one(); + assert!(FR_VAL_0_SERIALIZED_LSB == serialize(&val_0), 1); + assert!(FR_VAL_1_SERIALIZED_LSB == serialize(&val_1), 1); + let val_7 = from_u64(7); + let val_7_2nd = std::option::extract(&mut deserialize(&FR_VAL_7_SERIALIZED_LSB)); + let val_7_3rd = std::option::extract(&mut deserialize(&FR_VAL_7_SERIALIZED_MSB)); + assert!(eq(&val_7, &val_7_2nd), 1); + assert!(eq(&val_7, &val_7_3rd), 1); + assert!(FR_VAL_7_SERIALIZED_LSB == serialize(&val_7), 1); + assert!(FR_VAL_7_SERIALIZED_MSB == serialize(&val_7), 1); + + // Deserialization should fail if given a byte array of right size but the value is not a member. + assert!(std::option::is_none(&deserialize(&x"01000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73")), 1); + assert!(std::option::is_none(&deserialize(&x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")), 1); + + // Deserialization should fail if given a byte array of wrong size. + assert!(std::option::is_none(&deserialize(&x"01000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed7300")), 1); + assert!(std::option::is_none(&deserialize(&x"0073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")), 1); + assert!(std::option::is_none(&deserialize(&x"ffff")), 1); + assert!(std::option::is_none(&deserialize(&x"ffff")), 1); + + // Negation. + let val_minus_7 = neg(&val_7); + assert!(FR_VAL_7_NEG_SERIALIZED_LSB == serialize(&val_minus_7), 1); + + // Addition. + let val_9 = from_u64(9); + let val_2 = from_u64(2); + assert!(eq(&val_2, &add(&val_minus_7, &val_9)), 1); + + // Subtraction. + assert!(eq(&val_9, &sub(&val_2, &val_minus_7)), 1); + + // Multiplication. + let val_63 = from_u64(63); + assert!(eq(&val_63, &mul(&val_7, &val_9)), 1); + + // division. + let val_0 = from_u64(0); + assert!(eq(&val_7, &std::option::extract(&mut div(&val_63, &val_9))), 1); + assert!(std::option::is_none(&div(&val_63, &val_0)), 1); + + // Inversion. + assert!(eq(&val_minus_7, &neg(&val_7)), 1); + assert!(std::option::is_none(&inv(&val_0)), 1); + + // Squaring. + let val_x = rand_insecure(); + assert!(eq(&mul(&val_x, &val_x), &sqr(&val_x)), 1); + } + + #[test(fx = @std)] + fun test_pairing(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // pairing(a*P,b*Q) == (a*b)*pairing(P,Q) + let element_p = rand_insecure(); + let element_q = rand_insecure(); + let a = rand_insecure(); + let b = rand_insecure(); + let gt_element = pairing(&scalar_mul(&element_p, &a), &scalar_mul(&element_q, &b)); + let gt_element_another = scalar_mul(&pairing(&element_p, &element_q), &mul(&a, &b)); + assert!(eq(>_element, >_element_another), 1); + } + + #[test(fx = @std)] + fun test_multi_pairing(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Will compute e(a0*P0,b0*Q0)+e(a1*P1,b1*Q1)+e(a2*P2,b2*Q2). + let a0 = rand_insecure(); + let a1 = rand_insecure(); + let a2 = rand_insecure(); + let element_p0 = rand_insecure(); + let element_p1 = rand_insecure(); + let element_p2 = rand_insecure(); + let p0_a0 = scalar_mul(&element_p0, &a0); + let p1_a1 = scalar_mul(&element_p1, &a1); + let p2_a2 = scalar_mul(&element_p2, &a2); + let b0 = rand_insecure(); + let b1 = rand_insecure(); + let b2 = rand_insecure(); + let element_q0 = rand_insecure(); + let element_q1 = rand_insecure(); + let element_q2 = rand_insecure(); + let q0_b0 = scalar_mul(&element_q0, &b0); + let q1_b1 = scalar_mul(&element_q1, &b1); + let q2_b2 = scalar_mul(&element_q2, &b2); + + // Naive method. + let n0 = pairing(&p0_a0, &q0_b0); + let n1 = pairing(&p1_a1, &q1_b1); + let n2 = pairing(&p2_a2, &q2_b2); + let n = zero(); + n = add(&n, &n0); + n = add(&n, &n1); + n = add(&n, &n2); + + // Efficient API. + let m = multi_pairing(&vector[p0_a0, p1_a1, p2_a2], &vector[q0_b0, q1_b1, q2_b2]); + assert!(eq(&n, &m), 1); + } + + #[test(fx = @std)] + #[expected_failure(abort_code = 0x010002, location = aptos_std::crypto_algebra)] + fun test_multi_pairing_should_abort_when_sizes_mismatch(fx: signer) { + enable_cryptography_algebra_natives(&fx); + let g1_elements = vector[rand_insecure()]; + let g2_elements = vector[rand_insecure(), rand_insecure()]; + multi_pairing(&g1_elements, &g2_elements); + } + + #[test(fx = @std)] + #[expected_failure(abort_code = 0x010002, location = aptos_std::crypto_algebra)] + fun test_multi_scalar_mul_should_abort_when_sizes_mismatch(fx: signer) { + enable_cryptography_algebra_natives(&fx); + let elements = vector[rand_insecure()]; + let scalars = vector[rand_insecure(), rand_insecure()]; + multi_scalar_mul(&elements, &scalars); + } + + // + // (Tests end here.) + // +} diff --git a/aptos-move/framework/aptos-stdlib/sources/cryptography/crypto_algebra.move b/aptos-move/framework/aptos-stdlib/sources/cryptography/crypto_algebra.move new file mode 100644 index 0000000000000..4b8145e04dc73 --- /dev/null +++ b/aptos-move/framework/aptos-stdlib/sources/cryptography/crypto_algebra.move @@ -0,0 +1,344 @@ +/// This module provides generic structs/functions for operations of algebraic structures (e.g. fields and groups), +/// which can be used to build generic cryptographic schemes atop. +/// E.g., a Groth16 ZK proof verifier can be built to work over any pairing supported in this module. +/// +/// In general, every structure implements basic operations like (de)serialization, equality check, random sampling. +/// +/// A group may also implement the following operations. (Additive group notation is assumed.) +/// - `order()` for getting the group order. +/// - `zero()` for getting the group identity. +/// - `one()` for getting the group generator (if exists). +/// - `neg()` for group element inversion. +/// - `add()` for group operation (i.e., a group addition). +/// - `sub()` for group element subtraction. +/// - `double()` for efficient doubling. +/// - `scalar_mul()` for group scalar multiplication. +/// - `multi_scalar_mul()` for efficient group multi-scalar multiplication. +/// - `hash_to()` for hash-to-group. +/// +/// A field may also implement the following operations. +/// - `zero()` for getting the field additive identity. +/// - `one()` for getting the field multiplicative identity. +/// - `add()` for field addition. +/// - `sub()` for field subtraction. +/// - `mul()` for field multiplication. +/// - `div()` for field division. +/// - `neg()` for field negation. +/// - `inv()` for field inversion. +/// - `sqr()` for efficient field element squaring. +/// - `from_u64()` for quick conversion from u64 to field element. +/// +/// For 3 groups that admit a bilinear map, `pairing()` and `multi_pairing()` may be implemented. +/// +/// For a subset/superset relationship between 2 structures, `upcast()` and `downcast()` may be implemented. +/// E.g., in BLS12-381 pairing, since `Gt` is a subset of `Fq12`, +/// `upcast()` and `downcast()` will be supported. +/// +/// See `*_algebra.move` for currently implemented algebraic structures. +module aptos_std::crypto_algebra { + use std::option::{Option, some, none}; + use std::features; + + const E_NOT_IMPLEMENTED: u64 = 1; + const E_NON_EQUAL_LENGTHS: u64 = 2; + + /// This struct represents an element of a structure `S`. + struct Element has copy, drop { + handle: u64 + } + + // + // Public functions begin. + // + + /// Check if `x == y` for elements `x` and `y` of a structure `S`. + public fun eq(x: &Element, y: &Element): bool { + abort_unless_cryptography_algebra_natives_enabled(); + eq_internal(x.handle, y.handle) + } + + /// Convert a u64 to an element of a structure `S`. + public fun from_u64(value: u64): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: from_u64_internal(value) + } + } + + /// Return the additive identity of field `S`, or the identity of group `S`. + public fun zero(): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: zero_internal() + } + } + + /// Return the multiplicative identity of field `S`, or a fixed generator of group `S`. + public fun one(): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: one_internal() + } + } + + /// Compute `-x` for an element `x` of a structure `S`. + public fun neg(x: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: neg_internal(x.handle) + } + } + + /// Compute `x + y` for elements `x` and `y` of structure `S`. + public fun add(x: &Element, y: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: add_internal(x.handle, y.handle) + } + } + + /// Compute `x - y` for elements `x` and `y` of a structure `S`. + public fun sub(x: &Element, y: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: sub_internal(x.handle, y.handle) + } + } + + /// Compute `x * y` for elements `x` and `y` of a structure `S`. + public fun mul(x: &Element, y: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: mul_internal(x.handle, y.handle) + } + } + + /// Try computing `x / y` for elements `x` and `y` of a structure `S`. + /// Return none if `y` does not have a multiplicative inverse in the structure `S` + /// (e.g., when `S` is a field, and `y` is zero). + public fun div(x: &Element, y: &Element): Option> { + abort_unless_cryptography_algebra_natives_enabled(); + let (succ, handle) = div_internal(x.handle, y.handle); + if (succ) { + some(Element { handle }) + } else { + none() + } + } + + /// Compute `x^2` for an element `x` of a structure `S`. Faster and cheaper than `mul(x, x)`. + public fun sqr(x: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: sqr_internal(x.handle) + } + } + + /// Try computing `x^(-1)` for an element `x` of a structure `S`. + /// Return none if `x` does not have a multiplicative inverse in the structure `S` + /// (e.g., when `S` is a field, and `x` is zero). + public fun inv(x: &Element): Option> { + abort_unless_cryptography_algebra_natives_enabled(); + let (succeeded, handle) = inv_internal(x.handle); + if (succeeded) { + let scalar = Element { handle }; + some(scalar) + } else { + none() + } + } + + /// Compute `2*P` for an element `P` of a structure `S`. Faster and cheaper than `add(P, P)`. + public fun double(element_p: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: double_internal(element_p.handle) + } + } + + /// Compute `k[0]*P[0]+...+k[n-1]*P[n-1]`, where + /// `P[]` are `n` elements of group `G` represented by parameter `elements`, and + /// `k[]` are `n` elements of the scalarfield `S` of group `G` represented by parameter `scalars`. + /// + /// Abort with code `std::error::invalid_argument(E_NON_EQUAL_LENGTHS)` if the sizes of `elements` and `scalars` do not match. + public fun multi_scalar_mul(elements: &vector>, scalars: &vector>): Element { + let element_handles = handles_from_elements(elements); + let scalar_handles = handles_from_elements(scalars); + Element { + handle: multi_scalar_mul_internal(element_handles, scalar_handles) + } + } + + /// Compute `k*P`, where `P` is an element of a group `G` and `k` is an element of the scalar field `S` associated to the group `G`. + public fun scalar_mul(element_p: &Element, scalar_k: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: scalar_mul_internal(element_p.handle, scalar_k.handle) + } + } + + /// Efficiently compute `e(P[0],Q[0])+...+e(P[n-1],Q[n-1])`, + /// where `e: (G1,G2) -> (Gt)` is the pairing function from groups `(G1,G2)` to group `Gt`, + /// `P[]` are `n` elements of group `G1` represented by parameter `g1_elements`, and + /// `Q[]` are `n` elements of group `G2` represented by parameter `g2_elements`. + /// + /// Abort with code `std::error::invalid_argument(E_NON_EQUAL_LENGTHS)` if the sizes of `g1_elements` and `g2_elements` do not match. + /// + /// NOTE: we are viewing the target group `Gt` of the pairing as an additive group, + /// rather than a multiplicative one (which is typically the case). + public fun multi_pairing(g1_elements: &vector>, g2_elements: &vector>): Element { + abort_unless_cryptography_algebra_natives_enabled(); + let g1_handles = handles_from_elements(g1_elements); + let g2_handles = handles_from_elements(g2_elements); + Element { + handle: multi_pairing_internal(g1_handles, g2_handles) + } + } + + /// Compute the pairing function (a.k.a., bilinear map) on a `G1` element and a `G2` element. + /// Return an element in the target group `Gt`. + public fun pairing(element_1: &Element, element_2: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: pairing_internal(element_1.handle, element_2.handle) + } + } + + /// Try deserializing a byte array to an element of an algebraic structure `S` using a given serialization format `F`. + /// Return none if the deserialization failed. + public fun deserialize(bytes: &vector): Option> { + abort_unless_cryptography_algebra_natives_enabled(); + let (succeeded, handle) = deserialize_internal(bytes); + if (succeeded) { + some(Element { handle }) + } else { + none() + } + } + + /// Serialize an element of an algebraic structure `S` to a byte array using a given serialization format `F`. + public fun serialize(element: &Element): vector { + abort_unless_cryptography_algebra_natives_enabled(); + serialize_internal(element.handle) + } + + /// Get the order of structure `S`, a big integer little-endian encoded as a byte array. + public fun order(): vector { + abort_unless_cryptography_algebra_natives_enabled(); + order_internal() + } + + /// Cast an element of a structure `S` to a parent structure `L`. + public fun upcast(element: &Element): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: upcast_internal(element.handle) + } + } + + /// Try casting an element `x` of a structure `L` to a sub-structure `S`. + /// Return none if `x` is not a member of `S`. + /// + /// NOTE: Membership check in `S` is performed inside, which can be expensive, depending on the structures `L` and `S`. + public fun downcast(element_x: &Element): Option> { + abort_unless_cryptography_algebra_natives_enabled(); + let (succ, new_handle) = downcast_internal(element_x.handle); + if (succ) { + some(Element { handle: new_handle }) + } else { + none() + } + } + + /// Hash an arbitrary-length byte array `msg` into structure `S` with a domain separation tag `dst` + /// using the given hash-to-structure suite `H`. + /// + /// NOTE: some hashing methods do not accept a `dst` and will abort if a non-empty one is provided. + public fun hash_to(dst: &vector, msg: &vector): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: hash_to_internal(dst, msg) + } + } + + #[test_only] + /// Generate a random element of an algebraic structure `S`. + public fun rand_insecure(): Element { + abort_unless_cryptography_algebra_natives_enabled(); + Element { + handle: rand_insecure_internal() + } + } + + // + // (Public functions end here.) + // Private functions begin. + // + + fun abort_unless_cryptography_algebra_natives_enabled() { + if (features::cryptography_algebra_enabled()) return; + abort(std::error::not_implemented(0)) + } + + #[test_only] + public fun enable_cryptography_algebra_natives(fx: &signer) { + std::features::change_feature_flags(fx, vector[std::features::get_cryptography_algebra_natives_feature()], vector[]); + } + + fun handles_from_elements(elements: &vector>): vector { + let num_elements = std::vector::length(elements); + let element_handles = std::vector::empty(); + let i = 0; + while (i < num_elements) { + std::vector::push_back(&mut element_handles, std::vector::borrow(elements, i).handle); + i = i + 1; + }; + element_handles + } + + // + // (Private functions end here.) + // Native functions begin. + // + + native fun add_internal(handle_1: u64, handle_2: u64): u64; + native fun deserialize_internal(bytes: &vector): (bool, u64); + native fun div_internal(handle_1: u64, handle_2: u64): (bool, u64); + native fun double_internal(element_handle: u64): u64; + native fun downcast_internal(handle: u64): (bool, u64); + native fun from_u64_internal(value: u64): u64; + native fun eq_internal(handle_1: u64, handle_2: u64): bool; + native fun hash_to_internal(dst: &vector, bytes: &vector): u64; + native fun inv_internal(handle: u64): (bool, u64); + #[test_only] + native fun rand_insecure_internal(): u64; + native fun mul_internal(handle_1: u64, handle_2: u64): u64; + native fun multi_pairing_internal(g1_handles: vector, g2_handles: vector): u64; + native fun multi_scalar_mul_internal(element_handles: vector, scalar_handles: vector): u64; + native fun neg_internal(handle: u64): u64; + native fun one_internal(): u64; + native fun order_internal(): vector; + native fun pairing_internal(g1_handle: u64, g2_handle: u64): u64; + native fun scalar_mul_internal(element_handle: u64, scalar_handle: u64): u64; + native fun serialize_internal(handle: u64): vector; + native fun sqr_internal(handle: u64): u64; + native fun sub_internal(handle_1: u64, handle_2: u64): u64; + native fun upcast_internal(handle: u64): u64; + native fun zero_internal(): u64; + + // + // (Native functions end here.) + // Tests begin. + // + + #[test_only] + struct MysteriousGroup {} + + #[test(fx = @std)] + #[expected_failure(abort_code = 0x0c0001, location = Self)] + fun test_generic_operation_should_abort_with_unsupported_structures(fx: signer) { + enable_cryptography_algebra_natives(&fx); + let _ = order(); + } + // Tests end. +} diff --git a/aptos-move/framework/aptos-stdlib/sources/cryptography/crypto_algebra.spec.move b/aptos-move/framework/aptos-stdlib/sources/cryptography/crypto_algebra.spec.move new file mode 100644 index 0000000000000..bd41b281dcd2d --- /dev/null +++ b/aptos-move/framework/aptos-stdlib/sources/cryptography/crypto_algebra.spec.move @@ -0,0 +1,90 @@ +spec aptos_std::crypto_algebra { + spec add_internal(handle_1: u64, handle_2: u64): u64 { + pragma opaque; + } + + spec deserialize_internal(bytes: &vector): (bool, u64) { + pragma opaque; + } + + spec div_internal(handle_1: u64, handle_2: u64): (bool, u64) { + pragma opaque; + } + + spec double_internal(element_handle: u64): u64 { + pragma opaque; + } + + spec downcast_internal(handle: u64): (bool, u64) { + pragma opaque; + } + + spec from_u64_internal(value: u64): u64 { + pragma opaque; + } + + spec eq_internal(handle_1: u64, handle_2: u64): bool { + pragma opaque; + } + + spec hash_to_internal(dst: &vector, bytes: &vector): u64 { + pragma opaque; + } + + spec inv_internal(handle: u64): (bool, u64) { + pragma opaque; + } + + spec mul_internal(handle_1: u64, handle_2: u64): u64 { + pragma opaque; + } + + spec multi_pairing_internal(g1_handles: vector, g2_handles: vector): u64 { + pragma opaque; + } + + spec multi_scalar_mul_internal(element_handles: vector, scalar_handles: vector): u64 { + pragma opaque; + } + + spec neg_internal(handle: u64): u64 { + pragma opaque; + } + + spec one_internal(): u64 { + pragma opaque; + } + + spec order_internal(): vector { + pragma opaque; + } + + spec pairing_internal(g1_handle: u64, g2_handle: u64): u64 { + pragma opaque; + } + + spec scalar_mul_internal(element_handle: u64, scalar_handle: u64): u64 { + pragma opaque; + } + + spec serialize_internal(handle: u64): vector { + pragma opaque; + } + + spec sqr_internal(handle: u64): u64 { + pragma opaque; + } + + spec sub_internal(handle_1: u64, handle_2: u64): u64 { + pragma opaque; + } + + spec upcast_internal(handle: u64): u64 { + pragma opaque; + } + + spec zero_internal(): u64 { + pragma opaque; + } + +} diff --git a/aptos-move/framework/move-stdlib/doc/features.md b/aptos-move/framework/move-stdlib/doc/features.md index 6b1ae436931c9..64f2d19b52fa6 100644 --- a/aptos-move/framework/move-stdlib/doc/features.md +++ b/aptos-move/framework/move-stdlib/doc/features.md @@ -148,7 +148,7 @@ Lifetime: transient -Whether generic algebra basic operation support in algebra.move are enabled. +Whether generic algebra basic operation support in crypto_algebra.move are enabled. Lifetime: transient diff --git a/aptos-move/framework/move-stdlib/sources/configs/features.move b/aptos-move/framework/move-stdlib/sources/configs/features.move index 11cc768a06c7f..fa92c6520b5e2 100644 --- a/aptos-move/framework/move-stdlib/sources/configs/features.move +++ b/aptos-move/framework/move-stdlib/sources/configs/features.move @@ -140,7 +140,7 @@ module std::features { is_enabled(DELEGATION_POOLS) } - /// Whether generic algebra basic operation support in `algebra.move` are enabled. + /// Whether generic algebra basic operation support in `crypto_algebra.move` are enabled. /// /// Lifetime: transient const CRYPTOGRAPHY_ALGEBRA_NATIVES: u64 = 12; diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/add.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/add.rs new file mode 100644 index 0000000000000..99739fcc10916 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/add.rs @@ -0,0 +1,73 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + ark_binary_op_internal, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{ + collections::VecDeque, + ops::{Add, Mul}, + rc::Rc, +}; + +pub fn add_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fr, + add, + gas_params.ark_bls12_381_fr_add * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + add, + gas_params.ark_bls12_381_fq12_add * NumArgs::one() + ), + Some(Structure::BLS12381G1) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::G1Projective, + add, + gas_params.ark_bls12_381_g1_proj_add * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::G2Projective, + add, + gas_params.ark_bls12_381_g2_proj_add * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + mul, + gas_params.ark_bls12_381_fq12_mul * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/div.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/div.rs new file mode 100644 index 0000000000000..fce24a13000e3 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/div.rs @@ -0,0 +1,67 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use num_traits::Zero; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, ops::Div, rc::Rc}; + +macro_rules! ark_div_internal { + ($context:expr, $args:ident, $ark_typ:ty, $ark_func:ident, $gas_eq:expr, $gas_div:expr) => {{ + let handle_2 = safely_pop_arg!($args, u64) as usize; + let handle_1 = safely_pop_arg!($args, u64) as usize; + safe_borrow_element!($context, handle_1, $ark_typ, element_1_ptr, element_1); + safe_borrow_element!($context, handle_2, $ark_typ, element_2_ptr, element_2); + $context.charge($gas_eq)?; + if element_2.is_zero() { + return Ok(smallvec![Value::bool(false), Value::u64(0_u64)]); + } + $context.charge($gas_div)?; + let new_element = element_1.$ark_func(element_2); + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::bool(true), Value::u64(new_handle as u64)]) + }}; +} + +pub fn div_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_div_internal!( + context, + args, + ark_bls12_381::Fr, + div, + gas_params.ark_bls12_381_fr_eq * NumArgs::one(), + gas_params.ark_bls12_381_fr_div * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_div_internal!( + context, + args, + ark_bls12_381::Fq12, + div, + gas_params.ark_bls12_381_fq12_eq * NumArgs::one(), + gas_params.ark_bls12_381_fq12_div * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/double.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/double.rs new file mode 100644 index 0000000000000..a511fecfc79ce --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/double.rs @@ -0,0 +1,57 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + ark_unary_op_internal, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use ark_ec::Group; +use ark_ff::Field; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +pub fn double_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381G1) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::G1Projective, + double, + gas_params.ark_bls12_381_g1_proj_double * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::G2Projective, + double, + gas_params.ark_bls12_381_g2_proj_double * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + square, + gas_params.ark_bls12_381_fq12_square * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/inv.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/inv.rs new file mode 100644 index 0000000000000..02769738fefb0 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/inv.rs @@ -0,0 +1,60 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use ark_ff::Field; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +macro_rules! ark_inverse_internal { + ($context:expr, $args:ident, $ark_typ:ty, $gas:expr) => {{ + let handle = safely_pop_arg!($args, u64) as usize; + safe_borrow_element!($context, handle, $ark_typ, element_ptr, element); + $context.charge($gas)?; + match element.inverse() { + Some(new_element) => { + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::bool(true), Value::u64(new_handle as u64)]) + }, + None => Ok(smallvec![Value::bool(false), Value::u64(0)]), + } + }}; +} + +pub fn inv_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_inverse_internal!( + context, + args, + ark_bls12_381::Fr, + gas_params.ark_bls12_381_fr_inv * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_inverse_internal!( + context, + args, + ark_bls12_381::Fq12, + gas_params.ark_bls12_381_fq12_inv * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/mod.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/mod.rs new file mode 100644 index 0000000000000..0408acb6b0dc1 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/mod.rs @@ -0,0 +1,37 @@ +// Copyright © Aptos Foundation + +pub mod add; +pub mod div; +pub mod double; +pub mod inv; +pub mod mul; +pub mod neg; +pub mod scalar_mul; +pub mod sqr; +pub mod sub; + +#[macro_export] +macro_rules! ark_binary_op_internal { + ($context:expr, $args:ident, $ark_typ:ty, $ark_func:ident, $gas:expr) => {{ + let handle_2 = safely_pop_arg!($args, u64) as usize; + let handle_1 = safely_pop_arg!($args, u64) as usize; + safe_borrow_element!($context, handle_1, $ark_typ, element_1_ptr, element_1); + safe_borrow_element!($context, handle_2, $ark_typ, element_2_ptr, element_2); + $context.charge($gas)?; + let new_element = element_1.$ark_func(element_2); + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }}; +} + +#[macro_export] +macro_rules! ark_unary_op_internal { + ($context:expr, $args:ident, $ark_typ:ty, $ark_func:ident, $gas:expr) => {{ + let handle = safely_pop_arg!($args, u64) as usize; + safe_borrow_element!($context, handle, $ark_typ, element_ptr, element); + $context.charge($gas)?; + let new_element = element.$ark_func(); + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }}; +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/mul.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/mul.rs new file mode 100644 index 0000000000000..55dd08961430f --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/mul.rs @@ -0,0 +1,48 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + ark_binary_op_internal, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, ops::Mul, rc::Rc}; + +pub fn mul_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fr, + mul, + gas_params.ark_bls12_381_fr_mul * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + mul, + gas_params.ark_bls12_381_fq12_mul * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/neg.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/neg.rs new file mode 100644 index 0000000000000..e574c7b102425 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/neg.rs @@ -0,0 +1,71 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + ark_unary_op_internal, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use ark_ff::Field; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, ops::Neg, rc::Rc}; + +pub fn neg_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::Fr, + neg, + gas_params.ark_bls12_381_fr_neg * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + neg, + gas_params.ark_bls12_381_fq12_neg * NumArgs::one() + ), + Some(Structure::BLS12381G1) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::G1Projective, + neg, + gas_params.ark_bls12_381_g1_proj_neg * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::G2Projective, + neg, + gas_params.ark_bls12_381_g2_proj_neg * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!(context, handle, ark_bls12_381::Fq12, element_ptr, element); + context.charge(gas_params.ark_bls12_381_fq12_inv * NumArgs::one())?; + let new_element = element.inverse().ok_or_else(abort_invariant_violated)?; + let new_handle = store_element!(context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/scalar_mul.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/scalar_mul.rs new file mode 100644 index 0000000000000..4d206e8f786ce --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/scalar_mul.rs @@ -0,0 +1,224 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, gas::GasParameters, AlgebraContext, Structure, + MOVE_ABORT_CODE_INPUT_VECTOR_SIZES_NOT_MATCHING, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{log2_ceil, SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use aptos_types::on_chain_config::FeatureFlag; +use ark_ec::{CurveGroup, Group}; +use ark_ff::Field; +use move_core_types::gas_algebra::{InternalGas, InternalGasPerArg, NumArgs}; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +fn feature_flag_of_group_scalar_mul( + group_opt: Option, + scalar_field_opt: Option, +) -> Option { + match (group_opt, scalar_field_opt) { + (Some(Structure::BLS12381G1), Some(Structure::BLS12381Fr)) + | (Some(Structure::BLS12381G2), Some(Structure::BLS12381Fr)) + | (Some(Structure::BLS12381Gt), Some(Structure::BLS12381Fr)) => { + Some(FeatureFlag::BLS12_381_STRUCTURES) + }, + _ => None, + } +} + +macro_rules! abort_unless_group_scalar_mul_enabled { + ($context:ident, $group_opt:expr, $scalar_field_opt:expr) => { + let flag_opt = feature_flag_of_group_scalar_mul($group_opt, $scalar_field_opt); + abort_unless_feature_flag_enabled!($context, flag_opt); + }; +} + +macro_rules! ark_scalar_mul_internal { + ($context:expr, $args:ident, $group_typ:ty, $scalar_typ:ty, $op:ident, $gas:expr) => {{ + let scalar_handle = safely_pop_arg!($args, u64) as usize; + let element_handle = safely_pop_arg!($args, u64) as usize; + safe_borrow_element!($context, element_handle, $group_typ, element_ptr, element); + safe_borrow_element!($context, scalar_handle, $scalar_typ, scalar_ptr, scalar); + let scalar_bigint: ark_ff::BigInteger256 = (*scalar).into(); + $context.charge($gas)?; + let new_element = element.$op(scalar_bigint); + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }}; +} + +/// WARNING: Be careful with the unwrap() below, if you modify this if statement. +fn ark_msm_window_size(num_entries: usize) -> usize { + if num_entries < 32 { + 3 + } else { + (log2_ceil(num_entries).unwrap() * 69 / 100) + 2 + } +} + +/// The approximate cost model of https://github.com/arkworks-rs/algebra/blob/v0.4.0/ec/src/scalar_mul/variable_base/mod.rs#L89. +pub fn ark_msm_bigint_wnaf_cost( + cost_add: InternalGasPerArg, + cost_double: InternalGasPerArg, + num_entries: usize, +) -> InternalGas { + let window_size = ark_msm_window_size(num_entries); + let num_windows = (255 + window_size - 1) / window_size; + let num_buckets = 1_usize << window_size; + cost_add * NumArgs::from(((num_entries + num_buckets + 1) * num_windows) as u64) + + cost_double * NumArgs::from((num_buckets * num_windows) as u64) +} + +pub fn scalar_mul_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let group_opt = structure_from_ty_arg!(context, &ty_args[0]); + let scalar_field_opt = structure_from_ty_arg!(context, &ty_args[1]); + abort_unless_group_scalar_mul_enabled!(context, group_opt, scalar_field_opt); + match (group_opt, scalar_field_opt) { + (Some(Structure::BLS12381G1), Some(Structure::BLS12381Fr)) => { + ark_scalar_mul_internal!( + context, + args, + ark_bls12_381::G1Projective, + ark_bls12_381::Fr, + mul_bigint, + gas_params.ark_bls12_381_g1_proj_scalar_mul * NumArgs::one() + ) + }, + (Some(Structure::BLS12381G2), Some(Structure::BLS12381Fr)) => { + ark_scalar_mul_internal!( + context, + args, + ark_bls12_381::G2Projective, + ark_bls12_381::Fr, + mul_bigint, + gas_params.ark_bls12_381_g2_proj_scalar_mul * NumArgs::one() + ) + }, + (Some(Structure::BLS12381Gt), Some(Structure::BLS12381Fr)) => { + let scalar_handle = safely_pop_arg!(args, u64) as usize; + let element_handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!( + context, + element_handle, + ark_bls12_381::Fq12, + element_ptr, + element + ); + safe_borrow_element!( + context, + scalar_handle, + ark_bls12_381::Fr, + scalar_ptr, + scalar + ); + let scalar_bigint: ark_ff::BigInteger256 = (*scalar).into(); + context.charge(gas_params.ark_bls12_381_fq12_pow_u256 * NumArgs::one())?; + let new_element = element.pow(scalar_bigint); + let new_handle = store_element!(context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} + +macro_rules! ark_msm_internal { + ( + $context:expr, + $args:ident, + $proj_to_affine_cost:expr, + $proj_add_cost:expr, + $proj_double_cost:expr, + $element_typ:ty, + $scalar_typ:ty + ) => {{ + let scalar_handles = safely_pop_arg!($args, Vec); + let element_handles = safely_pop_arg!($args, Vec); + let num_elements = element_handles.len(); + let num_scalars = scalar_handles.len(); + if num_elements != num_scalars { + return Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_INPUT_VECTOR_SIZES_NOT_MATCHING, + }); + } + let mut bases = Vec::with_capacity(num_elements); + $context.charge($proj_to_affine_cost * NumArgs::from(num_elements as u64))?; + for handle in element_handles { + safe_borrow_element!( + $context, + handle as usize, + $element_typ, + element_ptr, + element + ); + bases.push(element.into_affine()); + } + let mut scalars = Vec::with_capacity(num_scalars); + for handle in scalar_handles { + safe_borrow_element!($context, handle as usize, $scalar_typ, scalar_ptr, scalar); + scalars.push(scalar.clone()); + } + $context.charge(ark_msm_bigint_wnaf_cost( + $proj_add_cost, + $proj_double_cost, + num_elements, + ))?; + let new_element: $element_typ = + ark_ec::VariableBaseMSM::msm(bases.as_slice(), scalars.as_slice()).unwrap(); + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }}; +} + +pub fn multi_scalar_mul_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + let scalar_opt = structure_from_ty_arg!(context, &ty_args[1]); + abort_unless_group_scalar_mul_enabled!(context, structure_opt, scalar_opt); + match (structure_opt, scalar_opt) { + (Some(Structure::BLS12381G1), Some(Structure::BLS12381Fr)) => { + ark_msm_internal!( + context, + args, + gas_params.ark_bls12_381_g1_proj_to_affine, + gas_params.ark_bls12_381_g1_proj_add, + gas_params.ark_bls12_381_g1_proj_double, + ark_bls12_381::G1Projective, + ark_bls12_381::Fr + ) + }, + (Some(Structure::BLS12381G2), Some(Structure::BLS12381Fr)) => { + ark_msm_internal!( + context, + args, + gas_params.ark_bls12_381_g2_proj_to_affine, + gas_params.ark_bls12_381_g2_proj_add, + gas_params.ark_bls12_381_g2_proj_double, + ark_bls12_381::G2Projective, + ark_bls12_381::Fr + ) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/sqr.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/sqr.rs new file mode 100644 index 0000000000000..6492bebb7316a --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/sqr.rs @@ -0,0 +1,48 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + ark_unary_op_internal, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use ark_ff::Field; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +pub fn sqr_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::Fr, + square, + gas_params.ark_bls12_381_fr_square * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_unary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + square, + gas_params.ark_bls12_381_fq12_square * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/sub.rs b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/sub.rs new file mode 100644 index 0000000000000..769deccc994a8 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/arithmetics/sub.rs @@ -0,0 +1,73 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + ark_binary_op_internal, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{ + collections::VecDeque, + ops::{Div, Sub}, + rc::Rc, +}; + +pub fn sub_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fr, + sub, + gas_params.ark_bls12_381_fr_sub * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + sub, + gas_params.ark_bls12_381_fq12_sub * NumArgs::one() + ), + Some(Structure::BLS12381G1) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::G1Projective, + sub, + gas_params.ark_bls12_381_g1_proj_sub * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::G2Projective, + sub, + gas_params.ark_bls12_381_g2_proj_sub * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => ark_binary_op_internal!( + context, + args, + ark_bls12_381::Fq12, + div, + gas_params.ark_bls12_381_fq12_div * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/casting.rs b/aptos-move/framework/src/natives/cryptography/algebra/casting.rs new file mode 100644 index 0000000000000..58fd75e0cb02e --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/casting.rs @@ -0,0 +1,87 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, gas::GasParameters, AlgebraContext, Structure, + BLS12381_R_SCALAR, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, structure_from_ty_arg, +}; +use aptos_types::on_chain_config::FeatureFlag; +use ark_ff::Field; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use num_traits::One; +use smallvec::{smallvec, SmallVec}; +use std::collections::VecDeque; + +fn feature_flag_of_casting( + super_opt: Option, + sub_opt: Option, +) -> Option { + match (super_opt, sub_opt) { + (Some(Structure::BLS12381Fq12), Some(Structure::BLS12381Gt)) => { + Some(FeatureFlag::BLS12_381_STRUCTURES) + }, + _ => None, + } +} + +macro_rules! abort_unless_casting_enabled { + ($context:ident, $super_opt:expr, $sub_opt:expr) => { + let flag_opt = feature_flag_of_casting($super_opt, $sub_opt); + abort_unless_feature_flag_enabled!($context, flag_opt); + }; +} + +pub fn downcast_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let super_opt = structure_from_ty_arg!(context, &ty_args[0]); + let sub_opt = structure_from_ty_arg!(context, &ty_args[1]); + abort_unless_casting_enabled!(context, super_opt, sub_opt); + match (super_opt, sub_opt) { + (Some(Structure::BLS12381Fq12), Some(Structure::BLS12381Gt)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!(context, handle, ark_bls12_381::Fq12, element_ptr, element); + context.charge(gas_params.ark_bls12_381_fq12_pow_u256 * NumArgs::one())?; + if element.pow(BLS12381_R_SCALAR.0) == ark_bls12_381::Fq12::one() { + Ok(smallvec![Value::bool(true), Value::u64(handle as u64)]) + } else { + Ok(smallvec![Value::bool(false), Value::u64(handle as u64)]) + } + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} + +pub fn upcast_internal( + _gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let sub_opt = structure_from_ty_arg!(context, &ty_args[0]); + let super_opt = structure_from_ty_arg!(context, &ty_args[1]); + abort_unless_casting_enabled!(context, super_opt, sub_opt); + match (sub_opt, super_opt) { + (Some(Structure::BLS12381Gt), Some(Structure::BLS12381Fq12)) => { + let handle = safely_pop_arg!(args, u64); + Ok(smallvec![Value::u64(handle)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/constants.rs b/aptos-move/framework/src/natives/cryptography/algebra/constants.rs new file mode 100644 index 0000000000000..984cabd70a96b --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/constants.rs @@ -0,0 +1,145 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + feature_flag_from_structure, gas::GasParameters, AlgebraContext, Structure, + BLS12381_GT_GENERATOR, BLS12381_Q12_LENDIAN, BLS12381_R_LENDIAN, + MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + store_element, structure_from_ty_arg, +}; +use ark_ec::Group; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use num_traits::{One, Zero}; +use once_cell::sync::Lazy; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +macro_rules! ark_constant_op_internal { + ($context:expr, $ark_typ:ty, $ark_func:ident, $gas:expr) => {{ + $context.charge($gas)?; + let new_element = <$ark_typ>::$ark_func(); + let new_handle = store_element!($context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }}; +} + +pub fn zero_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut _args: VecDeque, +) -> SafeNativeResult> { + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_constant_op_internal!( + context, + ark_bls12_381::Fr, + zero, + gas_params.ark_bls12_381_fr_zero * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_constant_op_internal!( + context, + ark_bls12_381::Fq12, + zero, + gas_params.ark_bls12_381_fq12_zero * NumArgs::one() + ), + Some(Structure::BLS12381G1) => ark_constant_op_internal!( + context, + ark_bls12_381::G1Projective, + zero, + gas_params.ark_bls12_381_g1_proj_infinity * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_constant_op_internal!( + context, + ark_bls12_381::G2Projective, + zero, + gas_params.ark_bls12_381_g2_proj_infinity * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => ark_constant_op_internal!( + context, + ark_bls12_381::Fq12, + one, + gas_params.ark_bls12_381_fq12_one * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} + +pub fn one_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut _args: VecDeque, +) -> SafeNativeResult> { + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_constant_op_internal!( + context, + ark_bls12_381::Fr, + one, + gas_params.ark_bls12_381_fr_one * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_constant_op_internal!( + context, + ark_bls12_381::Fq12, + one, + gas_params.ark_bls12_381_fq12_one * NumArgs::one() + ), + Some(Structure::BLS12381G1) => ark_constant_op_internal!( + context, + ark_bls12_381::G1Projective, + generator, + gas_params.ark_bls12_381_g1_proj_generator * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_constant_op_internal!( + context, + ark_bls12_381::G2Projective, + generator, + gas_params.ark_bls12_381_g2_proj_generator * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => { + context.charge(gas_params.ark_bls12_381_fq12_clone * NumArgs::one())?; + let element = *Lazy::force(&BLS12381_GT_GENERATOR); + let handle = store_element!(context, element); + Ok(smallvec![Value::u64(handle as u64)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} + +pub fn order_internal( + _gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut _args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) + | Some(Structure::BLS12381G1) + | Some(Structure::BLS12381G2) + | Some(Structure::BLS12381Gt) => { + Ok(smallvec![Value::vector_u8(BLS12381_R_LENDIAN.clone())]) + }, + Some(Structure::BLS12381Fq12) => { + Ok(smallvec![Value::vector_u8(BLS12381_Q12_LENDIAN.clone())]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/eq.rs b/aptos-move/framework/src/natives/cryptography/algebra/eq.rs new file mode 100644 index 0000000000000..8f93b7f0e10b8 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/eq.rs @@ -0,0 +1,75 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, feature_flag_from_structure, gas::GasParameters, + AlgebraContext, Structure, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, structure_from_ty_arg, +}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::collections::VecDeque; + +macro_rules! ark_eq_internal { + ($context:ident, $args:ident, $ark_typ:ty, $gas:expr) => {{ + let handle_2 = safely_pop_arg!($args, u64) as usize; + let handle_1 = safely_pop_arg!($args, u64) as usize; + safe_borrow_element!($context, handle_1, $ark_typ, element_1_ptr, element_1); + safe_borrow_element!($context, handle_2, $ark_typ, element_2_ptr, element_2); + $context.charge($gas)?; + let result = element_1 == element_2; + Ok(smallvec![Value::bool(result)]) + }}; +} + +pub fn eq_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => ark_eq_internal!( + context, + args, + ark_bls12_381::Fr, + gas_params.ark_bls12_381_fr_eq * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => ark_eq_internal!( + context, + args, + ark_bls12_381::Fq12, + gas_params.ark_bls12_381_fq12_eq * NumArgs::one() + ), + Some(Structure::BLS12381G1) => ark_eq_internal!( + context, + args, + ark_bls12_381::G1Projective, + gas_params.ark_bls12_381_g1_proj_eq * NumArgs::one() + ), + Some(Structure::BLS12381G2) => ark_eq_internal!( + context, + args, + ark_bls12_381::G2Projective, + gas_params.ark_bls12_381_g2_proj_eq * NumArgs::one() + ), + Some(Structure::BLS12381Gt) => ark_eq_internal!( + context, + args, + ark_bls12_381::Fq12, + gas_params.ark_bls12_381_fq12_eq * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/gas.rs b/aptos-move/framework/src/natives/cryptography/algebra/gas.rs index f0a54777c158f..dabc1ac8c43a8 100644 --- a/aptos-move/framework/src/natives/cryptography/algebra/gas.rs +++ b/aptos-move/framework/src/natives/cryptography/algebra/gas.rs @@ -3,5 +3,74 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 +use move_core_types::gas_algebra::InternalGasPerArg; + #[derive(Debug, Clone)] -pub struct GasParameters {} +pub struct GasParameters { + pub ark_bls12_381_fr_add: InternalGasPerArg, + pub ark_bls12_381_fr_deser: InternalGasPerArg, + pub ark_bls12_381_fr_div: InternalGasPerArg, + pub ark_bls12_381_fr_eq: InternalGasPerArg, + pub ark_bls12_381_fr_from_u64: InternalGasPerArg, + pub ark_bls12_381_fr_inv: InternalGasPerArg, + pub ark_bls12_381_fr_mul: InternalGasPerArg, + pub ark_bls12_381_fr_neg: InternalGasPerArg, + pub ark_bls12_381_fr_one: InternalGasPerArg, + pub ark_bls12_381_fr_serialize: InternalGasPerArg, + pub ark_bls12_381_fr_square: InternalGasPerArg, + pub ark_bls12_381_fr_sub: InternalGasPerArg, + pub ark_bls12_381_fr_zero: InternalGasPerArg, + pub ark_bls12_381_fq12_add: InternalGasPerArg, + pub ark_bls12_381_fq12_clone: InternalGasPerArg, + pub ark_bls12_381_fq12_deser: InternalGasPerArg, + pub ark_bls12_381_fq12_div: InternalGasPerArg, + pub ark_bls12_381_fq12_eq: InternalGasPerArg, + pub ark_bls12_381_fq12_from_u64: InternalGasPerArg, + pub ark_bls12_381_fq12_inv: InternalGasPerArg, + pub ark_bls12_381_fq12_mul: InternalGasPerArg, + pub ark_bls12_381_fq12_neg: InternalGasPerArg, + pub ark_bls12_381_fq12_one: InternalGasPerArg, + pub ark_bls12_381_fq12_pow_u256: InternalGasPerArg, + pub ark_bls12_381_fq12_serialize: InternalGasPerArg, + pub ark_bls12_381_fq12_square: InternalGasPerArg, + pub ark_bls12_381_fq12_sub: InternalGasPerArg, + pub ark_bls12_381_fq12_zero: InternalGasPerArg, + pub ark_bls12_381_g1_affine_deser_comp: InternalGasPerArg, + pub ark_bls12_381_g1_affine_deser_uncomp: InternalGasPerArg, + pub ark_bls12_381_g1_affine_serialize_comp: InternalGasPerArg, + pub ark_bls12_381_g1_affine_serialize_uncomp: InternalGasPerArg, + pub ark_bls12_381_g1_proj_add: InternalGasPerArg, + pub ark_bls12_381_g1_proj_double: InternalGasPerArg, + pub ark_bls12_381_g1_proj_eq: InternalGasPerArg, + pub ark_bls12_381_g1_proj_generator: InternalGasPerArg, + pub ark_bls12_381_g1_proj_infinity: InternalGasPerArg, + pub ark_bls12_381_g1_proj_neg: InternalGasPerArg, + pub ark_bls12_381_g1_proj_scalar_mul: InternalGasPerArg, + pub ark_bls12_381_g1_proj_sub: InternalGasPerArg, + pub ark_bls12_381_g1_proj_to_affine: InternalGasPerArg, + pub ark_bls12_381_g2_affine_deser_comp: InternalGasPerArg, + pub ark_bls12_381_g2_affine_deser_uncomp: InternalGasPerArg, + pub ark_bls12_381_g2_affine_serialize_comp: InternalGasPerArg, + pub ark_bls12_381_g2_affine_serialize_uncomp: InternalGasPerArg, + pub ark_bls12_381_g2_proj_add: InternalGasPerArg, + pub ark_bls12_381_g2_proj_double: InternalGasPerArg, + pub ark_bls12_381_g2_proj_eq: InternalGasPerArg, + pub ark_bls12_381_g2_proj_generator: InternalGasPerArg, + pub ark_bls12_381_g2_proj_infinity: InternalGasPerArg, + pub ark_bls12_381_g2_proj_neg: InternalGasPerArg, + pub ark_bls12_381_g2_proj_scalar_mul: InternalGasPerArg, + pub ark_bls12_381_g2_proj_sub: InternalGasPerArg, + pub ark_bls12_381_g2_proj_to_affine: InternalGasPerArg, + pub ark_bls12_381_pairing: InternalGasPerArg, + pub ark_bls12_381_multi_pairing_base: InternalGasPerArg, + pub ark_bls12_381_multi_pairing_per_pair: InternalGasPerArg, + pub ark_h2c_bls12381g1_xmd_sha256_sswu_base: InternalGasPerArg, + pub ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte: InternalGasPerArg, + pub ark_h2c_bls12381g2_xmd_sha256_sswu_base: InternalGasPerArg, + pub ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte: InternalGasPerArg, +} + +pub struct HashToGasParameters { + pub algebra: GasParameters, + pub sha2: aptos_move_stdlib::natives::hash::Sha2_256GasParameters, +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/hash_to_structure.rs b/aptos-move/framework/src/natives/cryptography/algebra/hash_to_structure.rs new file mode 100644 index 0000000000000..21df9cfa3d635 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/hash_to_structure.rs @@ -0,0 +1,138 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + gas::HashToGasParameters, AlgebraContext, HashToStructureSuite, Structure, + MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safely_pop_arg, store_element, structure_from_ty_arg, +}; +use aptos_types::on_chain_config::FeatureFlag; +use ark_ec::hashing::HashToCurve; +use move_core_types::gas_algebra::{ + InternalGas, InternalGasPerArg, InternalGasPerByte, NumArgs, NumBytes, +}; +use move_vm_types::{ + loaded_data::runtime_types::Type, + values::{Value, VectorRef}, +}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +fn feature_flag_of_hash_to_structure( + structure_opt: Option, + suite_opt: Option, +) -> Option { + match (structure_opt, suite_opt) { + (Some(Structure::BLS12381G1), Some(HashToStructureSuite::Bls12381g1XmdSha256SswuRo)) + | (Some(Structure::BLS12381G2), Some(HashToStructureSuite::Bls12381g2XmdSha256SswuRo)) => { + Some(FeatureFlag::BLS12_381_STRUCTURES) + }, + _ => None, + } +} + +macro_rules! abort_unless_hash_to_structure_enabled { + ($context:ident, $structure_opt:expr, $suite_opt:expr) => { + let flag_opt = feature_flag_of_hash_to_structure($structure_opt, $suite_opt); + abort_unless_feature_flag_enabled!($context, flag_opt); + }; +} + +macro_rules! suite_from_ty_arg { + ($context:expr, $typ:expr) => {{ + let type_tag = $context.type_to_type_tag($typ).unwrap(); + HashToStructureSuite::try_from(type_tag).ok() + }}; +} + +fn hash_to_bls12381gx_cost( + dst_len: usize, + msg_len: usize, + dst_shortening_base: InternalGas, + dst_shortening_per_byte: InternalGasPerByte, + mapping_base: InternalGasPerArg, + mapping_per_byte: InternalGasPerArg, +) -> InternalGas { + // DST shortening as defined in https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-using-dsts-longer-than-255-. + let dst_shortening_cost = if dst_len <= 255 { + InternalGas::zero() + } else { + dst_shortening_base + dst_shortening_per_byte * NumBytes::from((17 + dst_len) as u64) + }; + + // Mapping cost. The gas formula is simplified by assuming the DST length is fixed at 256. + let mapping_cost = + mapping_base * NumArgs::one() + mapping_per_byte * NumArgs::from(msg_len as u64); + + dst_shortening_cost + mapping_cost +} + +pub fn hash_to_internal( + gas_params: &HashToGasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + let suite_opt = suite_from_ty_arg!(context, &ty_args[1]); + abort_unless_hash_to_structure_enabled!(context, structure_opt, suite_opt); + let vector_ref = safely_pop_arg!(args, VectorRef); + let bytes_ref = vector_ref.as_bytes_ref(); + let msg = bytes_ref.as_slice(); + let tag_ref = safely_pop_arg!(args, VectorRef); + let bytes_ref = tag_ref.as_bytes_ref(); + let dst = bytes_ref.as_slice(); + match (structure_opt, suite_opt) { + (Some(Structure::BLS12381G1), Some(HashToStructureSuite::Bls12381g1XmdSha256SswuRo)) => { + context.charge(hash_to_bls12381gx_cost( + dst.len(), + msg.len(), + gas_params.sha2.base, + gas_params.sha2.per_byte, + gas_params.algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_base, + gas_params + .algebra + .ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte, + ))?; + let mapper = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + ark_ec::models::short_weierstrass::Projective, + ark_ff::fields::field_hashers::DefaultFieldHasher, + ark_ec::hashing::curve_maps::wb::WBMap, + >::new(dst) + .unwrap(); + let new_element = ::from(mapper.hash(msg).unwrap()); + let new_handle = store_element!(context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }, + (Some(Structure::BLS12381G2), Some(HashToStructureSuite::Bls12381g2XmdSha256SswuRo)) => { + context.charge(hash_to_bls12381gx_cost( + dst.len(), + msg.len(), + gas_params.sha2.base, + gas_params.sha2.per_byte, + gas_params.algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_base, + gas_params + .algebra + .ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte, + ))?; + let mapper = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + ark_ec::models::short_weierstrass::Projective, + ark_ff::fields::field_hashers::DefaultFieldHasher, + ark_ec::hashing::curve_maps::wb::WBMap, + >::new(dst) + .unwrap(); + let new_element = ::from(mapper.hash(msg).unwrap()); + let new_handle = store_element!(context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/mod.rs b/aptos-move/framework/src/natives/cryptography/algebra/mod.rs index 74681cec7b908..e17da5960f8e1 100644 --- a/aptos-move/framework/src/natives/cryptography/algebra/mod.rs +++ b/aptos-move/framework/src/natives/cryptography/algebra/mod.rs @@ -3,34 +3,66 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 -use crate::{ - natives::{ - cryptography::algebra::gas::GasParameters, - helpers::{make_safe_native, SafeNativeContext, SafeNativeError, SafeNativeResult}, +#[cfg(feature = "testing")] +use crate::natives::cryptography::algebra::rand::rand_insecure_internal; +#[cfg(feature = "testing")] +use crate::natives::helpers::make_test_only_native_from_func; +use crate::natives::{ + cryptography::algebra::{ + arithmetics::{ + add::add_internal, double::double_internal, mul::mul_internal, neg::neg_internal, + sqr::sqr_internal, sub::sub_internal, + }, + casting::{downcast_internal, upcast_internal}, + constants::{one_internal, order_internal, zero_internal}, + eq::eq_internal, + gas::HashToGasParameters, + hash_to_structure::hash_to_internal, + new::from_u64_internal, + pairing::{multi_pairing_internal, pairing_internal}, + serialization::{deserialize_internal, serialize_internal}, }, - safely_pop_arg, + helpers::make_safe_native, }; use aptos_types::on_chain_config::{FeatureFlag, Features, TimedFeatures}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use arithmetics::{ + div::div_internal, + inv::inv_internal, + scalar_mul::{multi_scalar_mul_internal, scalar_mul_internal}, +}; +use ark_serialize::CanonicalDeserialize; use better_any::{Tid, TidAble}; -use move_core_types::language_storage::TypeTag; +use move_binary_format::errors::PartialVMError; +use move_core_types::{language_storage::TypeTag, vm_status::StatusCode}; use move_vm_runtime::native_functions::NativeFunction; -use move_vm_types::{ - loaded_data::runtime_types::Type, - natives::function::{PartialVMError, StatusCode}, - values::{Value, VectorRef}, -}; -use smallvec::{smallvec, SmallVec}; -use std::{any::Any, collections::VecDeque, hash::Hash, ops::Add, rc::Rc, sync::Arc}; +use once_cell::sync::Lazy; +use std::{any::Any, hash::Hash, rc::Rc, sync::Arc}; +pub mod arithmetics; +pub mod casting; +pub mod constants; +pub mod eq; pub mod gas; +pub mod hash_to_structure; +pub mod new; +pub mod pairing; +#[cfg(feature = "testing")] +pub mod rand; +pub mod serialization; + +/// Equivalent to `std::error::invalid_argument(0)` in Move. +const MOVE_ABORT_CODE_INPUT_VECTOR_SIZES_NOT_MATCHING: u64 = 0x01_0002; /// Equivalent to `std::error::not_implemented(0)` in Move. -const MOVE_ABORT_CODE_NOT_IMPLEMENTED: u64 = 0x0C_0000; +const MOVE_ABORT_CODE_NOT_IMPLEMENTED: u64 = 0x0C_0001; -/// This encodes an algebraic structure defined in `algebra_*.move`. +/// This encodes an algebraic structure defined in `*_algebra.move`. #[derive(Copy, Clone, Eq, Hash, PartialEq)] pub enum Structure { + BLS12381Fq12, + BLS12381G1, + BLS12381G2, + BLS12381Gt, BLS12381Fr, } @@ -39,18 +71,35 @@ impl TryFrom for Structure { fn try_from(value: TypeTag) -> Result { match value.to_string().as_str() { - // Should match the full path to struct `Fr` in `algebra_bls12381.move`. - "0x1::algebra_bls12381::Fr" => Ok(Structure::BLS12381Fr), + "0x1::bls12381_algebra::Fr" => Ok(Structure::BLS12381Fr), + "0x1::bls12381_algebra::Fq12" => Ok(Structure::BLS12381Fq12), + "0x1::bls12381_algebra::G1" => Ok(Structure::BLS12381G1), + "0x1::bls12381_algebra::G2" => Ok(Structure::BLS12381G2), + "0x1::bls12381_algebra::Gt" => Ok(Structure::BLS12381Gt), _ => Err(()), } } } -/// This encodes a supported serialization formats defined in `algebra_*.move`. +#[macro_export] +macro_rules! structure_from_ty_arg { + ($context:expr, $typ:expr) => {{ + let type_tag = $context.type_to_type_tag($typ)?; + Structure::try_from(type_tag).ok() + }}; +} + +/// This encodes a supported serialization format defined in `*_algebra.move`. #[derive(Copy, Clone, Eq, Hash, PartialEq)] pub enum SerializationFormat { - /// This refers to `format_bls12381fr_lsb()` in `algebra_bls12381.move`. + BLS12381Fq12LscLsb, + BLS12381G1Compressed, + BLS12381G1Uncompressed, + BLS12381G2Compressed, + BLS12381G2Uncompressed, + BLS12381Gt, BLS12381FrLsb, + BLS12381FrMsb, } impl TryFrom for SerializationFormat { @@ -58,29 +107,45 @@ impl TryFrom for SerializationFormat { fn try_from(value: TypeTag) -> Result { match value.to_string().as_str() { - // Should match `format_bls12381fr_lsb()` in `algebra_bls12381.move`. - "0x1::algebra_bls12381::FrFormatLsb" => Ok(SerializationFormat::BLS12381FrLsb), + "0x1::bls12381_algebra::FormatFq12LscLsb" => { + Ok(SerializationFormat::BLS12381Fq12LscLsb) + }, + "0x1::bls12381_algebra::FormatG1Uncompr" => { + Ok(SerializationFormat::BLS12381G1Uncompressed) + }, + "0x1::bls12381_algebra::FormatG1Compr" => Ok(SerializationFormat::BLS12381G1Compressed), + "0x1::bls12381_algebra::FormatG2Uncompr" => { + Ok(SerializationFormat::BLS12381G2Uncompressed) + }, + "0x1::bls12381_algebra::FormatG2Compr" => Ok(SerializationFormat::BLS12381G2Compressed), + "0x1::bls12381_algebra::FormatGt" => Ok(SerializationFormat::BLS12381Gt), + "0x1::bls12381_algebra::FormatFrLsb" => Ok(SerializationFormat::BLS12381FrLsb), + "0x1::bls12381_algebra::FormatFrMsb" => Ok(SerializationFormat::BLS12381FrMsb), _ => Err(()), } } } -fn feature_flag_of_single_type_basic_op(structure_opt: Option) -> Option { - match structure_opt { - Some(Structure::BLS12381Fr) => Some(FeatureFlag::BLS12_381_STRUCTURES), - _ => None, - } +/// This encodes a supported hash-to-structure suite defined in `*_algebra.move`. +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub enum HashToStructureSuite { + Bls12381g1XmdSha256SswuRo, + Bls12381g2XmdSha256SswuRo, } -fn feature_flag_of_serialization_format( - structure_opt: Option, - format_opt: Option, -) -> Option { - match (structure_opt, format_opt) { - (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrLsb)) => { - Some(FeatureFlag::BLS12_381_STRUCTURES) - }, - _ => None, +impl TryFrom for HashToStructureSuite { + type Error = (); + + fn try_from(value: TypeTag) -> Result { + match value.to_string().as_str() { + "0x1::bls12381_algebra::HashG1XmdSha256SswuRo" => { + Ok(HashToStructureSuite::Bls12381g1XmdSha256SswuRo) + }, + "0x1::bls12381_algebra::HashG2XmdSha256SswuRo" => { + Ok(HashToStructureSuite::Bls12381g2XmdSha256SswuRo) + }, + _ => Err(()), + } } } @@ -95,65 +160,10 @@ impl AlgebraContext { } } -macro_rules! structure_from_ty_arg { - ($context:expr, $typ:expr) => {{ - let type_tag = $context.type_to_type_tag($typ)?; - Structure::try_from(type_tag).ok() - }}; -} - -macro_rules! format_from_ty_arg { - ($context:expr, $typ:expr) => {{ - let type_tag = $context.type_to_type_tag($typ)?; - SerializationFormat::try_from(type_tag).ok() - }}; -} - -macro_rules! store_element { - ($context:expr, $obj:expr) => {{ - let target_vec = &mut $context.extensions_mut().get_mut::().objs; - let ret = target_vec.len(); - target_vec.push(Rc::new($obj)); - ret - }}; -} - -macro_rules! abort_unless_feature_flag_enabled { - ($context:ident, $flag_opt:expr) => { - match $flag_opt { - Some(flag) if $context.get_feature_flags().is_enabled(flag) => { - // Continue. - }, - _ => { - return Err(SafeNativeError::Abort { - abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, - }); - }, - } - }; -} - -macro_rules! abort_unless_single_type_basic_op_enabled { - ($context:ident, $structure_opt:expr) => { - let flag_opt = feature_flag_of_single_type_basic_op($structure_opt); - abort_unless_feature_flag_enabled!($context, flag_opt); - }; -} - -macro_rules! abort_unless_serialization_format_enabled { - ($context:ident, $structure_opt:expr, $format_opt:expr) => { - let flag_opt = feature_flag_of_serialization_format($structure_opt, $format_opt); - abort_unless_feature_flag_enabled!($context, flag_opt); - }; -} - -fn abort_invariant_violated() -> PartialVMError { - PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) -} - /// Try getting a pointer to the `handle`-th elements in `context` and assign it to a local variable `ptr_out`. /// Then try casting it to a reference of `typ` and assign it in a local variable `ref_out`. /// Abort the VM execution with invariant violation if anything above fails. +#[macro_export] macro_rules! safe_borrow_element { ($context:expr, $handle:expr, $typ:ty, $ptr_out:ident, $ref_out:ident) => { let $ptr_out = $context @@ -169,161 +179,75 @@ macro_rules! safe_borrow_element { }; } -/// Macros that implements `serialize_internal()` using arkworks libraries. -macro_rules! ark_serialize_internal { - ( - $gas_params:expr, - $context:expr, - $args:ident, - $structure:expr, - $format:expr, - $ark_type:ty, - $ark_ser_func:ident - ) => {{ - let handle = safely_pop_arg!($args, u64) as usize; - safe_borrow_element!($context, handle, $ark_type, element_ptr, element); - let mut buf = vec![]; - match element.$ark_ser_func(&mut buf) { - Ok(_) => {}, - _ => { - abort_invariant_violated(); - unreachable!() - }, - } - buf +#[macro_export] +macro_rules! store_element { + ($context:expr, $obj:expr) => {{ + let target_vec = &mut $context.extensions_mut().get_mut::().objs; + let ret = target_vec.len(); + target_vec.push(Rc::new($obj)); + ret }}; } -fn serialize_internal( - _gas_params: &GasParameters, - context: &mut SafeNativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> SafeNativeResult> { - assert_eq!(2, ty_args.len()); - let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); - let format_opt = format_from_ty_arg!(context, &ty_args[1]); - abort_unless_serialization_format_enabled!(context, structure_opt, format_opt); - match (structure_opt, format_opt) { - (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrLsb)) => { - let buf = ark_serialize_internal!( - gas_params, - context, - args, - Structure::BLS12381Fr, - SerializationFormat::BLS12381FrLsb, - ark_bls12_381::Fr, - serialize_uncompressed - ); - Ok(smallvec![Value::vector_u8(buf)]) - }, - _ => Err(SafeNativeError::Abort { - abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, - }), +fn feature_flag_from_structure(structure_opt: Option) -> Option { + match structure_opt { + Some(Structure::BLS12381Fr) + | Some(Structure::BLS12381Fq12) + | Some(Structure::BLS12381G1) + | Some(Structure::BLS12381G2) + | Some(Structure::BLS12381Gt) => Some(FeatureFlag::BLS12_381_STRUCTURES), + _ => None, } } -/// Macros that implements `deserialize_internal()` using arkworks libraries. -macro_rules! ark_deserialize_internal { - ( - $gas_params:expr, - $context:expr, - $structure:expr, - $bytes:expr, - $format:expr, - $ark_typ:ty, - $ark_deser_func:ident - ) => {{ - match <$ark_typ>::$ark_deser_func($bytes) { - Ok(element) => { - let handle = store_element!($context, element); - Ok(smallvec![Value::bool(true), Value::u64(handle as u64)]) - }, - Err(ark_serialize::SerializationError::InvalidData) - | Err(ark_serialize::SerializationError::UnexpectedFlags) => { - Ok(smallvec![Value::bool(false), Value::u64(0)]) +#[macro_export] +macro_rules! abort_unless_arithmetics_enabled_for_structure { + ($context:ident, $structure_opt:expr) => { + let flag_opt = feature_flag_from_structure($structure_opt); + abort_unless_feature_flag_enabled!($context, flag_opt); + }; +} + +#[macro_export] +macro_rules! abort_unless_feature_flag_enabled { + ($context:ident, $flag_opt:expr) => { + match $flag_opt { + Some(flag) if $context.get_feature_flags().is_enabled(flag) => { + // Continue. }, _ => { - abort_invariant_violated(); - unreachable!() + return Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }); }, } - }}; + }; } -fn deserialize_internal( - _gas_params: &GasParameters, - context: &mut SafeNativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> SafeNativeResult> { - assert_eq!(2, ty_args.len()); - let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); - let format_opt = format_from_ty_arg!(context, &ty_args[1]); - abort_unless_serialization_format_enabled!(context, structure_opt, format_opt); - let vector_ref = safely_pop_arg!(args, VectorRef); - let bytes_ref = vector_ref.as_bytes_ref(); - let bytes = bytes_ref.as_slice(); - match (structure_opt, format_opt) { - (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrLsb)) => { - if bytes.len() != 32 { - return Ok(smallvec![Value::bool(false), Value::u64(0)]); - } - ark_deserialize_internal!( - gas_params, - context, - Structure::BLS12381Fr, - bytes, - SerializationFormat::BLS12381FrLsb, - ark_bls12_381::Fr, - deserialize_uncompressed - ) - }, - _ => Err(SafeNativeError::Abort { - abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, - }), - } +fn abort_invariant_violated() -> PartialVMError { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) } -macro_rules! ark_add_internal { - ($gas_params:expr, $context:expr, $args:ident, $structure:expr, $ark_type:ty) => {{ - let handle_2 = safely_pop_arg!($args, u64) as usize; - let handle_1 = safely_pop_arg!($args, u64) as usize; - safe_borrow_element!($context, handle_1, $ark_type, element_1_ptr, element_1); - safe_borrow_element!($context, handle_2, $ark_type, element_2_ptr, element_2); - let new_element = element_1.add(element_2); - let new_handle = store_element!($context, new_element); - Ok(smallvec![Value::u64(new_handle as u64)]) - }}; -} +static BLS12381_GT_GENERATOR: Lazy = Lazy::new(|| { + let buf = hex::decode("b68917caaa0543a808c53908f694d1b6e7b38de90ce9d83d505ca1ef1b442d2727d7d06831d8b2a7920afc71d8eb50120f17a0ea982a88591d9f43503e94a8f1abaf2e4589f65aafb7923c484540a868883432a5c60e75860b11e5465b1c9a08873ec29e844c1c888cb396933057ffdd541b03a5220eda16b2b3a6728ea678034ce39c6839f20397202d7c5c44bb68134f93193cec215031b17399577a1de5ff1f5b0666bdd8907c61a7651e4e79e0372951505a07fa73c25788db6eb8023519a5aa97b51f1cad1d43d8aabbff4dc319c79a58cafc035218747c2f75daf8f2fb7c00c44da85b129113173d4722f5b201b6b4454062e9ea8ba78c5ca3cadaf7238b47bace5ce561804ae16b8f4b63da4645b8457a93793cbd64a7254f150781019de87ee42682940f3e70a88683d512bb2c3fb7b2434da5dedbb2d0b3fb8487c84da0d5c315bdd69c46fb05d23763f2191aabd5d5c2e12a10b8f002ff681bfd1b2ee0bf619d80d2a795eb22f2aa7b85d5ffb671a70c94809f0dafc5b73ea2fb0657bae23373b4931bc9fa321e8848ef78894e987bff150d7d671aee30b3931ac8c50e0b3b0868effc38bf48cd24b4b811a2995ac2a09122bed9fd9fa0c510a87b10290836ad06c8203397b56a78e9a0c61c77e56ccb4f1bc3d3fcaea7550f3503efe30f2d24f00891cb45620605fcfaa4292687b3a7db7c1c0554a93579e889a121fd8f72649b2402996a084d2381c5043166673b3849e4fd1e7ee4af24aa8ed443f56dfd6b68ffde4435a92cd7a4ac3bc77e1ad0cb728606cf08bf6386e5410f").unwrap(); + ark_bls12_381::Fq12::deserialize_uncompressed(buf.as_slice()).unwrap() +}); -fn add_internal( - _gas_params: &GasParameters, - context: &mut SafeNativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> SafeNativeResult> { - assert_eq!(1, ty_args.len()); - let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); - abort_unless_single_type_basic_op_enabled!(context, structure_opt); - match structure_opt { - Some(Structure::BLS12381Fr) => { - ark_add_internal!( - gas_params, - context, - args, - Structure::BLS12381Fr, - ark_bls12_381::Fr - ) - }, - _ => Err(SafeNativeError::Abort { - abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, - }), - } -} +static BLS12381_R_LENDIAN: Lazy> = Lazy::new(|| { + hex::decode("01000000fffffffffe5bfeff02a4bd5305d8a10908d83933487d9d2953a7ed73").unwrap() +}); + +static BLS12381_R_SCALAR: Lazy = Lazy::new(|| { + ark_ff::BigInteger256::deserialize_uncompressed(BLS12381_R_LENDIAN.as_slice()).unwrap() +}); + +static BLS12381_Q12_LENDIAN: Lazy> = Lazy::new(|| { + hex::decode("1175f55da544c7625f8ccb1360e2b1d3ca40747811c8f5ed04440afe232b476c0215676aec05f2a44ac2da6b6d1b7cff075e7b2a587e0aab601a8d3db4f0d29906e5e4d0d78119f396d5a59f0f8d1ca8bca62540be6ab9c12d0ca00de1f311f106278d000e55a393c9766a74e0d08a298450f60d7e666575e3354bf14b8731f4e721c0c180a5ed55c2f8f51f815baecbf96b5fc717eb58ac161a27d1d5f2bdc1a079609b9d6449165b2466b32a01eac7992a1ea0cac2f223cde1d56f9bbccc67afe44621daf858df3fc0eb837818f3e42ab3e131ce4e492efa63c108e6ef91c29ed63b3045baebcb0ab8d203c7f558beaffccba31b12aca7f54b58d0c28340e4fdb3c7c94fe9c4fef9d640ff2fcff02f1748416cbed0981fbff49f0e39eaf8a30273e67ed851944d33d6a593ef5ddcd62da84568822a6045b633bf6a513b3cfe8f9de13e76f8dcbd915980dec205eab6a5c0c72dcebd9afff1d25509ddbf33f8e24131fbd74cda93336514340cf8036b66b09ed9e6a6ac37e22fb3ac407e321beae8cd9fe74c8aaeb4edaa9a7272848fc623f6fe835a2e647379f547fc5ec6371318a85bfa60009cb20ccbb8a467492988a87633c14c0324ba0d0c3e1798ed29c8494cea35023746da05e35d184b4a301d5b2238d665495c6318b5af8653758008952d06cb9e62487b196d64383c73c06d6e1cccdf9b3ce8f95679e7050d949004a55f4ccf95b2552880ae36d1f7e09504d2338316d87d14a064511a295d768113e301bdf9d4383a8be32192d3f2f3b2de14181c73839a7cb4af5301").unwrap() +}); pub fn make_all( - gas_params: GasParameters, + move_gas_params: aptos_move_stdlib::natives::GasParameters, + gas_params: crate::natives::cryptography::algebra::gas::GasParameters, timed_features: TimedFeatures, features: Arc, ) -> impl Iterator { @@ -340,6 +264,24 @@ pub fn make_all( deserialize_internal, ), ), + ( + "downcast_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + downcast_internal, + ), + ), + ( + "eq_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + eq_internal, + ), + ), ( "add_internal", make_safe_native( @@ -349,11 +291,174 @@ pub fn make_all( add_internal, ), ), + ( + "div_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + div_internal, + ), + ), + ( + "inv_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + inv_internal, + ), + ), + ( + "mul_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + mul_internal, + ), + ), + ( + "neg_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + neg_internal, + ), + ), + ( + "one_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + one_internal, + ), + ), + ( + "sqr_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + sqr_internal, + ), + ), + ( + "sub_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + sub_internal, + ), + ), + ( + "zero_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + zero_internal, + ), + ), + ( + "from_u64_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + from_u64_internal, + ), + ), + ( + "double_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + double_internal, + ), + ), + ( + "multi_scalar_mul_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + multi_scalar_mul_internal, + ), + ), + ( + "order_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + order_internal, + ), + ), + ( + "scalar_mul_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + scalar_mul_internal, + ), + ), + ( + "hash_to_internal", + make_safe_native( + HashToGasParameters { + algebra: gas_params.clone(), + sha2: move_gas_params.hash.sha2_256, + }, + timed_features.clone(), + features.clone(), + hash_to_internal, + ), + ), + ( + "multi_pairing_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + multi_pairing_internal, + ), + ), + ( + "pairing_internal", + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + pairing_internal, + ), + ), ( "serialize_internal", - make_safe_native(gas_params, timed_features, features, serialize_internal), + make_safe_native( + gas_params.clone(), + timed_features.clone(), + features.clone(), + serialize_internal, + ), + ), + ( + "upcast_internal", + make_safe_native(gas_params, timed_features, features, upcast_internal), ), ]); + // Test-only natives. + #[cfg(feature = "testing")] + natives.append(&mut vec![( + "rand_insecure_internal", + make_test_only_native_from_func(rand_insecure_internal), + )]); + crate::natives::helpers::make_module_natives(natives) } diff --git a/aptos-move/framework/src/natives/cryptography/algebra/new.rs b/aptos-move/framework/src/natives/cryptography/algebra/new.rs new file mode 100644 index 0000000000000..ededb95953be7 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/new.rs @@ -0,0 +1,55 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_arithmetics_enabled_for_structure, abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + feature_flag_from_structure, gas::GasParameters, AlgebraContext, Structure, + MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safely_pop_arg, store_element, structure_from_ty_arg, +}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +macro_rules! from_u64_internal { + ($context:expr, $args:ident, $typ:ty, $gas:expr) => {{ + let value = safely_pop_arg!($args, u64); + $context.charge($gas)?; + let element = <$typ>::from(value as u64); + let handle = store_element!($context, element); + Ok(smallvec![Value::u64(handle as u64)]) + }}; +} + +pub fn from_u64_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + abort_unless_arithmetics_enabled_for_structure!(context, structure_opt); + match structure_opt { + Some(Structure::BLS12381Fr) => from_u64_internal!( + context, + args, + ark_bls12_381::Fr, + gas_params.ark_bls12_381_fr_from_u64 * NumArgs::one() + ), + Some(Structure::BLS12381Fq12) => from_u64_internal!( + context, + args, + ark_bls12_381::Fq12, + gas_params.ark_bls12_381_fq12_from_u64 * NumArgs::one() + ), + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/pairing.rs b/aptos-move/framework/src/natives/cryptography/algebra/pairing.rs new file mode 100644 index 0000000000000..300db4beb56c5 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/pairing.rs @@ -0,0 +1,152 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, gas::GasParameters, AlgebraContext, Structure, + MOVE_ABORT_CODE_INPUT_VECTOR_SIZES_NOT_MATCHING, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use aptos_types::on_chain_config::FeatureFlag; +use ark_ec::{pairing::Pairing, CurveGroup}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{loaded_data::runtime_types::Type, values::Value}; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +fn feature_flag_of_pairing( + g1_opt: Option, + g2_opt: Option, + gt_opt: Option, +) -> Option { + match (g1_opt, g2_opt, gt_opt) { + (Some(Structure::BLS12381G1), Some(Structure::BLS12381G2), Some(Structure::BLS12381Gt)) => { + Some(FeatureFlag::BLS12_381_STRUCTURES) + }, + _ => None, + } +} + +macro_rules! abort_unless_pairing_enabled { + ($context:ident, $g1_opt:expr, $g2_opt:expr, $gt_opt:expr) => { + let flag_opt = feature_flag_of_pairing($g1_opt, $g2_opt, $gt_opt); + abort_unless_feature_flag_enabled!($context, flag_opt); + }; +} + +pub fn multi_pairing_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(3, ty_args.len()); + let g1_opt = structure_from_ty_arg!(context, &ty_args[0]); + let g2_opt = structure_from_ty_arg!(context, &ty_args[1]); + let gt_opt = structure_from_ty_arg!(context, &ty_args[2]); + abort_unless_pairing_enabled!(context, g1_opt, g2_opt, gt_opt); + match (g1_opt, g2_opt, gt_opt) { + (Some(Structure::BLS12381G1), Some(Structure::BLS12381G2), Some(Structure::BLS12381Gt)) => { + let g2_element_handles = safely_pop_arg!(args, Vec); + let g1_element_handles = safely_pop_arg!(args, Vec); + let num_entries = g1_element_handles.len(); + if num_entries != g2_element_handles.len() { + return Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_INPUT_VECTOR_SIZES_NOT_MATCHING, + }); + } + + context.charge( + gas_params.ark_bls12_381_g1_proj_to_affine * NumArgs::from(num_entries as u64), + )?; + let mut g1_elements_affine = Vec::with_capacity(num_entries); + for handle in g1_element_handles { + safe_borrow_element!( + context, + handle as usize, + ark_bls12_381::G1Projective, + ptr, + element + ); + g1_elements_affine.push(element.into_affine()); + } + + context.charge( + gas_params.ark_bls12_381_g2_proj_to_affine * NumArgs::from(num_entries as u64), + )?; + let mut g2_elements_affine = Vec::with_capacity(num_entries); + for handle in g2_element_handles { + safe_borrow_element!( + context, + handle as usize, + ark_bls12_381::G2Projective, + ptr, + element + ); + g2_elements_affine.push(element.into_affine()); + } + + context.charge( + gas_params.ark_bls12_381_multi_pairing_base * NumArgs::one() + + gas_params.ark_bls12_381_multi_pairing_per_pair + * NumArgs::from(num_entries as u64), + )?; + let new_element = + ark_bls12_381::Bls12_381::multi_pairing(g1_elements_affine, g2_elements_affine).0; + let new_handle = store_element!(context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} + +pub fn pairing_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(3, ty_args.len()); + let g1_opt = structure_from_ty_arg!(context, &ty_args[0]); + let g2_opt = structure_from_ty_arg!(context, &ty_args[1]); + let gt_opt = structure_from_ty_arg!(context, &ty_args[2]); + abort_unless_pairing_enabled!(context, g1_opt, g2_opt, gt_opt); + match (g1_opt, g2_opt, gt_opt) { + (Some(Structure::BLS12381G1), Some(Structure::BLS12381G2), Some(Structure::BLS12381Gt)) => { + let g2_element_handle = safely_pop_arg!(args, u64) as usize; + let g1_element_handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!( + context, + g1_element_handle, + ark_bls12_381::G1Projective, + g1_element_ptr, + g1_element + ); + context.charge(gas_params.ark_bls12_381_g1_proj_to_affine * NumArgs::one())?; + let g1_element_affine = g1_element.into_affine(); + safe_borrow_element!( + context, + g2_element_handle, + ark_bls12_381::G2Projective, + g2_element_ptr, + g2_element + ); + context.charge(gas_params.ark_bls12_381_g2_proj_to_affine * NumArgs::one())?; + let g2_element_affine = g2_element.into_affine(); + context.charge(gas_params.ark_bls12_381_pairing * NumArgs::one())?; + let new_element = + ark_bls12_381::Bls12_381::pairing(g1_element_affine, g2_element_affine).0; + let new_handle = store_element!(context, new_element); + Ok(smallvec![Value::u64(new_handle as u64)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/rand.rs b/aptos-move/framework/src/natives/cryptography/algebra/rand.rs new file mode 100644 index 0000000000000..4f85f399074a3 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/rand.rs @@ -0,0 +1,67 @@ +// Copyright © Aptos Foundation + +#[cfg(feature = "testing")] +use crate::{ + natives::cryptography::algebra::{AlgebraContext, Structure, BLS12381_GT_GENERATOR}, + store_element, structure_from_ty_arg, +}; +#[cfg(feature = "testing")] +use ark_ff::Field; +#[cfg(feature = "testing")] +use ark_std::{test_rng, UniformRand}; +use move_binary_format::errors::PartialVMResult; +use move_core_types::gas_algebra::InternalGas; +use move_vm_runtime::native_functions::NativeContext; +#[cfg(feature = "testing")] +use move_vm_types::{ + loaded_data::runtime_types::Type, natives::function::NativeResult, values::Value, +}; +#[cfg(feature = "testing")] +use smallvec::smallvec; +#[cfg(feature = "testing")] +use std::{collections::VecDeque, rc::Rc}; + +#[cfg(feature = "testing")] +macro_rules! ark_rand_internal { + ($context:expr, $typ:ty) => {{ + let element = <$typ>::rand(&mut test_rng()); + let handle = store_element!($context, element); + Ok(NativeResult::ok(InternalGas::zero(), smallvec![ + Value::u64(handle as u64) + ])) + }}; +} + +#[cfg(feature = "testing")] +pub fn rand_insecure_internal( + context: &mut NativeContext, + ty_args: Vec, + mut _args: VecDeque, +) -> PartialVMResult { + assert_eq!(1, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + match structure_opt { + Some(Structure::BLS12381Fr) => { + ark_rand_internal!(context, ark_bls12_381::Fr) + }, + Some(Structure::BLS12381Fq12) => { + ark_rand_internal!(context, ark_bls12_381::Fq12) + }, + Some(Structure::BLS12381G1) => { + ark_rand_internal!(context, ark_bls12_381::G1Projective) + }, + Some(Structure::BLS12381G2) => { + ark_rand_internal!(context, ark_bls12_381::G2Projective) + }, + Some(Structure::BLS12381Gt) => { + let k = ark_bls12_381::Fr::rand(&mut test_rng()); + let k_bigint: ark_ff::BigInteger256 = k.into(); + let element = BLS12381_GT_GENERATOR.pow(k_bigint); + let handle = store_element!(context, element); + Ok(NativeResult::ok(InternalGas::zero(), smallvec![ + Value::u64(handle as u64) + ])) + }, + _ => unreachable!(), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/algebra/serialization.rs b/aptos-move/framework/src/natives/cryptography/algebra/serialization.rs new file mode 100644 index 0000000000000..3976038bbca86 --- /dev/null +++ b/aptos-move/framework/src/natives/cryptography/algebra/serialization.rs @@ -0,0 +1,359 @@ +// Copyright © Aptos Foundation + +use crate::{ + abort_unless_feature_flag_enabled, + natives::{ + cryptography::algebra::{ + abort_invariant_violated, gas::GasParameters, AlgebraContext, SerializationFormat, + Structure, BLS12381_R_SCALAR, MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }, + helpers::{SafeNativeContext, SafeNativeError, SafeNativeResult}, + }, + safe_borrow_element, safely_pop_arg, store_element, structure_from_ty_arg, +}; +use aptos_types::on_chain_config::FeatureFlag; +use ark_ec::CurveGroup; +use ark_ff::Field; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use move_core_types::gas_algebra::NumArgs; +use move_vm_types::{ + loaded_data::runtime_types::Type, + values::{Value, VectorRef}, +}; +use num_traits::One; +use smallvec::{smallvec, SmallVec}; +use std::{collections::VecDeque, rc::Rc}; + +pub fn feature_flag_of_serialization_format( + format_opt: Option, +) -> Option { + match format_opt { + Some(SerializationFormat::BLS12381FrLsb) + | Some(SerializationFormat::BLS12381FrMsb) + | Some(SerializationFormat::BLS12381Fq12LscLsb) + | Some(SerializationFormat::BLS12381G1Uncompressed) + | Some(SerializationFormat::BLS12381G1Compressed) + | Some(SerializationFormat::BLS12381G2Uncompressed) + | Some(SerializationFormat::BLS12381G2Compressed) + | Some(SerializationFormat::BLS12381Gt) => Some(FeatureFlag::BLS12_381_STRUCTURES), + _ => None, + } +} + +macro_rules! abort_unless_serialization_format_enabled { + ($context:ident, $format_opt:expr) => { + let flag_opt = feature_flag_of_serialization_format($format_opt); + abort_unless_feature_flag_enabled!($context, flag_opt); + }; +} + +macro_rules! format_from_ty_arg { + ($context:expr, $typ:expr) => {{ + let type_tag = $context.type_to_type_tag($typ)?; + SerializationFormat::try_from(type_tag).ok() + }}; +} + +pub fn serialize_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + let format_opt = format_from_ty_arg!(context, &ty_args[1]); + abort_unless_serialization_format_enabled!(context, format_opt); + match (structure_opt, format_opt) { + (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrLsb)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!(context, handle, ark_bls12_381::Fr, element_ptr, element); + let mut buf = vec![]; + context.charge(gas_params.ark_bls12_381_fr_serialize * NumArgs::one())?; + element + .serialize_uncompressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrMsb)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!(context, handle, ark_bls12_381::Fr, element_ptr, element); + let mut buf = vec![]; + context.charge(gas_params.ark_bls12_381_fr_serialize * NumArgs::one())?; + element + .serialize_uncompressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + buf.reverse(); + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381Fq12), Some(SerializationFormat::BLS12381Fq12LscLsb)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!(context, handle, ark_bls12_381::Fq12, element_ptr, element); + let mut buf = vec![]; + context.charge(gas_params.ark_bls12_381_fq12_serialize * NumArgs::one())?; + element + .serialize_uncompressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381G1), Some(SerializationFormat::BLS12381G1Uncompressed)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!( + context, + handle, + ark_bls12_381::G1Projective, + element_ptr, + element + ); + let element_affine = element.into_affine(); + let mut buf = Vec::new(); + context.charge(gas_params.ark_bls12_381_g1_affine_serialize_uncomp * NumArgs::one())?; + element_affine + .serialize_uncompressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381G1), Some(SerializationFormat::BLS12381G1Compressed)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!( + context, + handle, + ark_bls12_381::G1Projective, + element_ptr, + element + ); + let element_affine = element.into_affine(); + let mut buf = Vec::new(); + context.charge(gas_params.ark_bls12_381_g1_affine_serialize_comp * NumArgs::one())?; + element_affine + .serialize_compressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381G2), Some(SerializationFormat::BLS12381G2Uncompressed)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!( + context, + handle, + ark_bls12_381::G2Projective, + element_ptr, + element + ); + let element_affine = element.into_affine(); + let mut buf = Vec::new(); + context.charge(gas_params.ark_bls12_381_g2_affine_serialize_uncomp * NumArgs::one())?; + element_affine + .serialize_uncompressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381G2), Some(SerializationFormat::BLS12381G2Compressed)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!( + context, + handle, + ark_bls12_381::G2Projective, + element_ptr, + element + ); + let element_affine = element.into_affine(); + let mut buf = Vec::new(); + context.charge(gas_params.ark_bls12_381_g2_affine_serialize_comp * NumArgs::one())?; + element_affine + .serialize_compressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + (Some(Structure::BLS12381Gt), Some(SerializationFormat::BLS12381Gt)) => { + let handle = safely_pop_arg!(args, u64) as usize; + safe_borrow_element!(context, handle, ark_bls12_381::Fq12, element_ptr, element); + let mut buf = vec![]; + context.charge(gas_params.ark_bls12_381_fq12_serialize * NumArgs::one())?; + element + .serialize_uncompressed(&mut buf) + .map_err(|_e| abort_invariant_violated())?; + Ok(smallvec![Value::vector_u8(buf)]) + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} + +/// Macros that implements `deserialize_internal()` using arkworks libraries. +macro_rules! ark_deserialize_internal { + ($context:expr, $bytes:expr, $ark_typ:ty, $ark_deser_func:ident, $gas:expr) => {{ + $context.charge($gas)?; + match <$ark_typ>::$ark_deser_func($bytes) { + Ok(element) => { + let handle = store_element!($context, element); + Ok(smallvec![Value::bool(true), Value::u64(handle as u64)]) + }, + Err(ark_serialize::SerializationError::InvalidData) + | Err(ark_serialize::SerializationError::UnexpectedFlags) => { + Ok(smallvec![Value::bool(false), Value::u64(0)]) + }, + _ => Err(SafeNativeError::InvariantViolation( + abort_invariant_violated(), + )), + } + }}; +} + +macro_rules! ark_ec_point_deserialize_internal { + ($context:expr, $bytes:expr, $typ:ty, $deser_func:ident, $gas:expr) => {{ + $context.charge($gas)?; + match <$typ>::$deser_func($bytes) { + Ok(element) => { + let element_proj = ark_ec::short_weierstrass::Projective::from(element); + let handle = store_element!($context, element_proj); + Ok(smallvec![Value::bool(true), Value::u64(handle as u64)]) + }, + Err(ark_serialize::SerializationError::InvalidData) + | Err(ark_serialize::SerializationError::UnexpectedFlags) => { + Ok(smallvec![Value::bool(false), Value::u64(0)]) + }, + _ => Err(SafeNativeError::InvariantViolation( + abort_invariant_violated(), + )), + } + }}; +} + +pub fn deserialize_internal( + gas_params: &GasParameters, + context: &mut SafeNativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> SafeNativeResult> { + assert_eq!(2, ty_args.len()); + let structure_opt = structure_from_ty_arg!(context, &ty_args[0]); + let format_opt = format_from_ty_arg!(context, &ty_args[1]); + abort_unless_serialization_format_enabled!(context, format_opt); + let vector_ref = safely_pop_arg!(args, VectorRef); + let bytes_ref = vector_ref.as_bytes_ref(); + let bytes = bytes_ref.as_slice(); + match (structure_opt, format_opt) { + (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrLsb)) => { + // Valid BLS12381FrLsb serialization should be 32-byte. + // NOTE: Arkworks deserialization cost grows as the input size grows. + // So exit early if the size is incorrect, for gas safety. (Also applied to other cases across this file.) + if bytes.len() != 32 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + ark_deserialize_internal!( + context, + bytes, + ark_bls12_381::Fr, + deserialize_uncompressed, + gas_params.ark_bls12_381_fr_deser * NumArgs::one() + ) + }, + (Some(Structure::BLS12381Fr), Some(SerializationFormat::BLS12381FrMsb)) => { + // Valid BLS12381FrMsb serialization should be 32-byte. + if bytes.len() != 32 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + let mut bytes_copy: Vec = bytes.to_vec(); + bytes_copy.reverse(); + let bytes = bytes_copy.as_slice(); + ark_deserialize_internal!( + context, + bytes, + ark_bls12_381::Fr, + deserialize_uncompressed, + gas_params.ark_bls12_381_fr_deser * NumArgs::one() + ) + }, + (Some(Structure::BLS12381Fq12), Some(SerializationFormat::BLS12381Fq12LscLsb)) => { + // Valid BLS12381Fq12LscLsb serialization should be 576-byte. + if bytes.len() != 576 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + ark_deserialize_internal!( + context, + bytes, + ark_bls12_381::Fq12, + deserialize_uncompressed, + gas_params.ark_bls12_381_fq12_deser * NumArgs::one() + ) + }, + (Some(Structure::BLS12381G1), Some(SerializationFormat::BLS12381G1Uncompressed)) => { + // Valid BLS12381G1AffineUncompressed serialization should be 96-byte. + if bytes.len() != 96 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + ark_ec_point_deserialize_internal!( + context, + bytes, + ark_bls12_381::G1Affine, + deserialize_uncompressed, + gas_params.ark_bls12_381_g1_affine_deser_uncomp * NumArgs::one() + ) + }, + (Some(Structure::BLS12381G1), Some(SerializationFormat::BLS12381G1Compressed)) => { + // Valid BLS12381G1AffineCompressed serialization should be 48-byte. + if bytes.len() != 48 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + ark_ec_point_deserialize_internal!( + context, + bytes, + ark_bls12_381::G1Affine, + deserialize_compressed, + gas_params.ark_bls12_381_g1_affine_deser_comp * NumArgs::one() + ) + }, + (Some(Structure::BLS12381G2), Some(SerializationFormat::BLS12381G2Uncompressed)) => { + // Valid BLS12381G2AffineUncompressed serialization should be 192-byte. + if bytes.len() != 192 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + ark_ec_point_deserialize_internal!( + context, + bytes, + ark_bls12_381::G2Affine, + deserialize_uncompressed, + gas_params.ark_bls12_381_g2_affine_deser_uncomp * NumArgs::one() + ) + }, + (Some(Structure::BLS12381G2), Some(SerializationFormat::BLS12381G2Compressed)) => { + // Valid BLS12381G2AffineCompressed serialization should be 96-byte. + if bytes.len() != 96 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + ark_ec_point_deserialize_internal!( + context, + bytes, + ark_bls12_381::G2Affine, + deserialize_compressed, + gas_params.ark_bls12_381_g2_affine_deser_comp * NumArgs::one() + ) + }, + (Some(Structure::BLS12381Gt), Some(SerializationFormat::BLS12381Gt)) => { + // Valid BLS12381Gt serialization should be 576-byte. + if bytes.len() != 576 { + return Ok(smallvec![Value::bool(false), Value::u64(0)]); + } + context.charge(gas_params.ark_bls12_381_fq12_deser * NumArgs::one())?; + match ::deserialize_uncompressed(bytes) { + Ok(element) => { + context.charge( + (gas_params.ark_bls12_381_fq12_pow_u256 + gas_params.ark_bls12_381_fq12_eq) + * NumArgs::one(), + )?; + if element.pow(BLS12381_R_SCALAR.0) == ark_bls12_381::Fq12::one() { + let handle = store_element!(context, element); + Ok(smallvec![Value::bool(true), Value::u64(handle as u64)]) + } else { + Ok(smallvec![Value::bool(false), Value::u64(0)]) + } + }, + _ => Ok(smallvec![Value::bool(false), Value::u64(0)]), + } + }, + _ => Err(SafeNativeError::Abort { + abort_code: MOVE_ABORT_CODE_NOT_IMPLEMENTED, + }), + } +} diff --git a/aptos-move/framework/src/natives/cryptography/ristretto255_point.rs b/aptos-move/framework/src/natives/cryptography/ristretto255_point.rs index 3c39a891c3beb..9a32335ca7ebc 100644 --- a/aptos-move/framework/src/natives/cryptography/ristretto255_point.rs +++ b/aptos-move/framework/src/natives/cryptography/ristretto255_point.rs @@ -10,7 +10,7 @@ use crate::{ pop_64_byte_slice, pop_scalar_from_bytes, scalar_from_struct, GasParameters, COMPRESSED_POINT_NUM_BYTES, }, - helpers::{SafeNativeContext, SafeNativeResult}, + helpers::{log2_floor, SafeNativeContext, SafeNativeResult}, }, safely_assert_eq, safely_pop_arg, safely_pop_type_arg, }; @@ -594,54 +594,6 @@ pub(crate) fn native_multi_scalar_mul( Ok(smallvec![Value::u64(result_handle)]) } -/// For all $n > 0$, returns $\floor{\log_2{n}}$, contained within a `Some`. -/// For $n = 0$, returns `None`. -#[allow(non_snake_case)] -fn log2_floor(n: usize) -> Option { - if n == 0 { - return None; - } - - // NOTE: n > 0, so n.leading_zeros() cannot equal usize::BITS. Therefore, we will never cast -1 to a usize. - Some(((usize::BITS - n.leading_zeros()) - 1) as usize) -} - -#[cfg(test)] -mod test { - use crate::natives::cryptography::ristretto255_point::log2_floor; - - #[test] - fn test_log2_floor() { - assert_eq!(log2_floor(usize::MIN), None); - assert_eq!(log2_floor(0), None); - assert_eq!(log2_floor(1), Some(0)); - - assert_eq!(log2_floor(2), Some(1)); - assert_eq!(log2_floor(3), Some(1)); - - assert_eq!(log2_floor(4), Some(2)); - assert_eq!(log2_floor(5), Some(2)); - assert_eq!(log2_floor(6), Some(2)); - assert_eq!(log2_floor(7), Some(2)); - - assert_eq!(log2_floor(8), Some(3)); - assert_eq!(log2_floor(9), Some(3)); - assert_eq!(log2_floor(10), Some(3)); - assert_eq!(log2_floor(11), Some(3)); - assert_eq!(log2_floor(12), Some(3)); - assert_eq!(log2_floor(13), Some(3)); - assert_eq!(log2_floor(14), Some(3)); - assert_eq!(log2_floor(15), Some(3)); - - assert_eq!(log2_floor(16), Some(4)); - - // usize::MAX = 2^{usize::BITS} - 1, so the floor will be usize::BITS - 1 - assert_eq!(log2_floor(usize::MAX), Some((usize::BITS - 1) as usize)); - - println!("All good."); - } -} - /// This upgrades 'native_multi_scalar_mul' in two ways: /// 1. It is a "safe" native that uses `SafeNativeContext::charge` to prevent DoS attacks. /// 2. It no longer uses floating-point arithmetic to compute the gas costs. diff --git a/aptos-move/framework/src/natives/helpers.rs b/aptos-move/framework/src/natives/helpers.rs index f2d9d14d751de..e71d3767c1141 100644 --- a/aptos-move/framework/src/natives/helpers.rs +++ b/aptos-move/framework/src/natives/helpers.rs @@ -280,3 +280,70 @@ where Arc::new(closure) } + +/// For all $n > 0$, returns $\floor{\log_2{n}}$, contained within a `Some`. +/// For $n = 0$, returns `None`. +pub fn log2_floor(n: usize) -> Option { + if n == 0 { + return None; + } + + // NOTE: n > 0, so n.leading_zeros() cannot equal usize::BITS. Therefore, we will never cast -1 to a usize. + Some(((usize::BITS - n.leading_zeros()) - 1) as usize) +} + +#[test] +fn test_log2_floor() { + assert_eq!(log2_floor(usize::MIN), None); + assert_eq!(log2_floor(0), None); + assert_eq!(log2_floor(1), Some(0)); + assert_eq!(log2_floor(2), Some(1)); + assert_eq!(log2_floor(3), Some(1)); + assert_eq!(log2_floor(4), Some(2)); + assert_eq!(log2_floor(5), Some(2)); + assert_eq!(log2_floor(6), Some(2)); + assert_eq!(log2_floor(7), Some(2)); + assert_eq!(log2_floor(8), Some(3)); + assert_eq!(log2_floor(9), Some(3)); + assert_eq!(log2_floor(10), Some(3)); + assert_eq!(log2_floor(11), Some(3)); + assert_eq!(log2_floor(12), Some(3)); + assert_eq!(log2_floor(13), Some(3)); + assert_eq!(log2_floor(14), Some(3)); + assert_eq!(log2_floor(15), Some(3)); + assert_eq!(log2_floor(16), Some(4)); + assert_eq!(log2_floor(usize::MAX), Some((usize::BITS - 1) as usize)); +} + +/// For all $n > 0$, returns $\ceil{\log_2{n}}$, contained within a `Some`. +/// For $n = 0$, returns `None`. +pub fn log2_ceil(n: usize) -> Option { + match n { + 0 => None, + 1 => Some(0), + _ => Some(log2_floor(n - 1).unwrap() + 1), + } +} + +#[test] +fn test_log2_ceil() { + assert_eq!(log2_ceil(usize::MIN), None); + assert_eq!(log2_ceil(0), None); + assert_eq!(log2_ceil(1), Some(0)); + assert_eq!(log2_ceil(2), Some(1)); + assert_eq!(log2_ceil(3), Some(2)); + assert_eq!(log2_ceil(4), Some(2)); + assert_eq!(log2_ceil(5), Some(3)); + assert_eq!(log2_ceil(6), Some(3)); + assert_eq!(log2_ceil(7), Some(3)); + assert_eq!(log2_ceil(8), Some(3)); + assert_eq!(log2_ceil(9), Some(4)); + assert_eq!(log2_ceil(10), Some(4)); + assert_eq!(log2_ceil(11), Some(4)); + assert_eq!(log2_ceil(12), Some(4)); + assert_eq!(log2_ceil(13), Some(4)); + assert_eq!(log2_ceil(14), Some(4)); + assert_eq!(log2_ceil(15), Some(4)); + assert_eq!(log2_ceil(16), Some(4)); + assert_eq!(log2_ceil(usize::MAX), Some(usize::BITS as usize)); +} diff --git a/aptos-move/framework/src/natives/mod.rs b/aptos-move/framework/src/natives/mod.rs index a38a4a0e35151..7028eefaea8fd 100644 --- a/aptos-move/framework/src/natives/mod.rs +++ b/aptos-move/framework/src/natives/mod.rs @@ -76,7 +76,69 @@ impl GasParameters { per_msg_hashing: 0.into(), per_byte_hashing: 0.into(), }, - algebra: cryptography::algebra::gas::GasParameters {}, + algebra: cryptography::algebra::gas::GasParameters { + ark_bls12_381_fr_serialize: 0.into(), + ark_bls12_381_fr_deser: 0.into(), + ark_bls12_381_fr_from_u64: 0.into(), + ark_bls12_381_fr_neg: 0.into(), + ark_bls12_381_fr_add: 0.into(), + ark_bls12_381_fr_sub: 0.into(), + ark_bls12_381_fr_mul: 0.into(), + ark_bls12_381_fr_inv: 0.into(), + ark_bls12_381_fr_div: 0.into(), + ark_bls12_381_fr_eq: 0.into(), + ark_bls12_381_g1_proj_infinity: 0.into(), + ark_bls12_381_g1_proj_generator: 0.into(), + ark_bls12_381_g1_affine_serialize_uncomp: 0.into(), + ark_bls12_381_g1_affine_deser_uncomp: 0.into(), + ark_bls12_381_g1_affine_serialize_comp: 0.into(), + ark_bls12_381_g1_affine_deser_comp: 0.into(), + ark_bls12_381_g1_proj_to_affine: 0.into(), + ark_bls12_381_g1_proj_neg: 0.into(), + ark_bls12_381_g1_proj_add: 0.into(), + ark_bls12_381_g1_proj_sub: 0.into(), + ark_bls12_381_g1_proj_scalar_mul: 0.into(), + ark_bls12_381_g1_proj_eq: 0.into(), + ark_bls12_381_g2_proj_infinity: 0.into(), + ark_bls12_381_g2_proj_generator: 0.into(), + ark_bls12_381_g2_affine_serialize_uncomp: 0.into(), + ark_bls12_381_g2_affine_deser_uncomp: 0.into(), + ark_bls12_381_g2_affine_serialize_comp: 0.into(), + ark_bls12_381_g2_affine_deser_comp: 0.into(), + ark_bls12_381_g2_proj_to_affine: 0.into(), + ark_bls12_381_g2_proj_neg: 0.into(), + ark_bls12_381_g2_proj_add: 0.into(), + ark_bls12_381_g2_proj_sub: 0.into(), + ark_bls12_381_g2_proj_scalar_mul: 0.into(), + ark_bls12_381_g2_proj_eq: 0.into(), + ark_bls12_381_fq12_serialize: 0.into(), + ark_bls12_381_fq12_eq: 0.into(), + ark_bls12_381_fq12_one: 0.into(), + ark_bls12_381_fq12_pow_u256: 0.into(), + ark_bls12_381_fq12_clone: 0.into(), + ark_bls12_381_fq12_deser: 0.into(), + ark_bls12_381_fq12_mul: 0.into(), + ark_bls12_381_fq12_sub: 0.into(), + ark_bls12_381_fq12_inv: 0.into(), + ark_bls12_381_fq12_square: 0.into(), + ark_bls12_381_g1_proj_double: 0.into(), + ark_bls12_381_g2_proj_double: 0.into(), + ark_bls12_381_fq12_div: 0.into(), + ark_bls12_381_fq12_add: 0.into(), + ark_bls12_381_fq12_from_u64: 0.into(), + ark_bls12_381_fq12_neg: 0.into(), + ark_bls12_381_fr_square: 0.into(), + ark_bls12_381_fr_one: 0.into(), + ark_bls12_381_fr_zero: 0.into(), + ark_bls12_381_fq12_zero: 0.into(), + ark_bls12_381_pairing: 0.into(), + ark_bls12_381_multi_pairing_base: 0.into(), + ark_bls12_381_multi_pairing_per_pair: 0.into(), + ark_h2c_bls12381g1_xmd_sha256_sswu_base: 0.into(), + ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte: 0.into(), + ark_h2c_bls12381g2_xmd_sha256_sswu_base: 0.into(), + ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte: 0.into(), + }, ed25519: ed25519::GasParameters { base: 0.into(), per_pubkey_deserialize: 0.into(), @@ -202,6 +264,7 @@ impl GasParameters { pub fn all_natives( framework_addr: AccountAddress, + move_gas_params: aptos_move_stdlib::natives::GasParameters, gas_params: GasParameters, timed_features: TimedFeatures, features: Arc, @@ -242,9 +305,10 @@ pub fn all_natives( ) ); add_natives_from_module!( - "algebra", + "crypto_algebra", cryptography::algebra::make_all( - gas_params.algebra.clone(), + move_gas_params, + gas_params.algebra, timed_features.clone(), features.clone() ) diff --git a/aptos-move/move-examples/groth16_example/Move.toml b/aptos-move/move-examples/groth16_example/Move.toml new file mode 100644 index 0000000000000..80a0c0a51c57a --- /dev/null +++ b/aptos-move/move-examples/groth16_example/Move.toml @@ -0,0 +1,6 @@ +[package] +name = "GenericGroth16" +version = "0.0.0" + +[dependencies] +AptosFramework = { local = "../../framework/aptos-framework" } diff --git a/aptos-move/move-examples/groth16_example/sources/groth16.move b/aptos-move/move-examples/groth16_example/sources/groth16.move new file mode 100644 index 0000000000000..360c5436f837b --- /dev/null +++ b/aptos-move/move-examples/groth16_example/sources/groth16.move @@ -0,0 +1,178 @@ +/// Generic implementation of Groth16 (proof verification) as defined in https://eprint.iacr.org/2016/260.pdf, Section 3.2. +/// Actual proof verifiers can be constructed using the pairings supported in the generic algebra module. +/// See the test cases in this module for an example of constructing with BLS12-381 curves. +module groth16_example::groth16 { + use aptos_std::crypto_algebra::{Element, from_u64, multi_scalar_mul, eq, multi_pairing, upcast, pairing, add, zero}; + + /// Proof verification as specified in the original paper, + /// with the following input (in the original paper notations). + /// - Verification key: $\left([\alpha]_1, [\beta]_2, [\gamma]_2, [\delta]_2, \left\\{ \left[ \frac{\beta \cdot u_i(x) + \alpha \cdot v_i(x) + w_i(x)}{\gamma} \right]_1 \right\\}\_{i=0}^l \right)$. + /// - Public inputs: $\\{a_i\\}_{i=1}^l$. + /// - Proof $\left( \left[ A \right]_1, \left[ B \right]_2, \left[ C \right]_1 \right)$. + public fun verify_proof( + vk_alpha_g1: &Element, + vk_beta_g2: &Element, + vk_gamma_g2: &Element, + vk_delta_g2: &Element, + vk_uvw_gamma_g1: &vector>, + public_inputs: &vector>, + proof_a: &Element, + proof_b: &Element, + proof_c: &Element, + ): bool { + let left = pairing(proof_a, proof_b); + let scalars = vector[from_u64(1)]; + std::vector::append(&mut scalars, *public_inputs); + let right = zero(); + let right = add(&right, &pairing(vk_alpha_g1, vk_beta_g2)); + let right = add(&right, &pairing(&multi_scalar_mul(vk_uvw_gamma_g1, &scalars), vk_gamma_g2)); + let right = add(&right, &pairing(proof_c, vk_delta_g2)); + eq(&left, &right) + } + + /// Modified proof verification which is optimized for low verification latency + /// but requires a pairing and 2 `G2` negations to be pre-computed. + /// Below are the full input (in the original paper notations). + /// - Prepared verification key: $\left([\alpha]_1 \cdot [\beta]_2, -[\gamma]_2, -[\delta]_2, \left\\{ \left[ \frac{\beta \cdot u_i(x) + \alpha \cdot v_i(x) + w_i(x)}{\gamma} \right]_1 \right\\}\_{i=0}^l \right)$. + /// - Public inputs: $\\{a_i\\}_{i=1}^l$. + /// - Proof: $\left( \left[ A \right]_1, \left[ B \right]_2, \left[ C \right]_1 \right)$. + public fun verify_proof_prepared( + pvk_alpha_g1_beta_g2: &Element, + pvk_gamma_g2_neg: &Element, + pvk_delta_g2_neg: &Element, + pvk_uvw_gamma_g1: &vector>, + public_inputs: &vector>, + proof_a: &Element, + proof_b: &Element, + proof_c: &Element, + ): bool { + let scalars = vector[from_u64(1)]; + std::vector::append(&mut scalars, *public_inputs); + let g1_elements = vector[*proof_a, multi_scalar_mul(pvk_uvw_gamma_g1, &scalars), *proof_c]; + let g2_elements = vector[*proof_b, *pvk_gamma_g2_neg, *pvk_delta_g2_neg]; + eq(pvk_alpha_g1_beta_g2, &multi_pairing(&g1_elements, &g2_elements)) + } + + /// A variant of `verify_proof_prepared()` that requires `pvk_alpha_g1_beta_g2` to be an element of `Fq12` instead of its subgroup `Gt`. + /// With this variant, the caller may save a `Gt` deserialization (which involves an expensive `Gt` membership test). + /// Below are the full input (in the original paper notations). + /// - Prepared verification key: $\left([\alpha]_1 \cdot [\beta]_2, -[\gamma]_2, -[\delta]_2, \left\\{ \left[ \frac{\beta \cdot u_i(x) + \alpha \cdot v_i(x) + w_i(x)}{\gamma} \right]_1 \right\\}\_{i=0}^l \right)$. + /// - Public inputs: $\\{a_i\\}_{i=1}^l$. + /// - Proof: $\left( \left[ A \right]_1, \left[ B \right]_2, \left[ C \right]_1 \right)$. + public fun verify_proof_prepared_fq12( + pvk_alpha_g1_beta_g2: &Element, + pvk_gamma_g2_neg: &Element, + pvk_delta_g2_neg: &Element, + pvk_uvw_gamma_g1: &vector>, + public_inputs: &vector>, + proof_a: &Element, + proof_b: &Element, + proof_c: &Element, + ): bool { + let scalars = vector[from_u64(1)]; + std::vector::append(&mut scalars, *public_inputs); + let g1_elements = vector[*proof_a, multi_scalar_mul(pvk_uvw_gamma_g1, &scalars), *proof_c]; + let g2_elements = vector[*proof_b, *pvk_gamma_g2_neg, *pvk_delta_g2_neg]; + eq(pvk_alpha_g1_beta_g2, &upcast(&multi_pairing(&g1_elements, &g2_elements))) + } + + #[test_only] + use aptos_std::crypto_algebra::{deserialize, enable_cryptography_algebra_natives}; + #[test_only] + use aptos_std::bls12381_algebra::{Fr, FormatFrLsb, FormatG1Compr, FormatG2Compr, FormatFq12LscLsb, G1, G2, Gt, Fq12, FormatGt}; + + #[test(fx = @std)] + fun test_verify_proof_with_bls12381(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Below is an example MIMC proof sampled from test case https://github.com/arkworks-rs/groth16/blob/b6f9166bcf15ff4bfe101bb34e1bdc0d92302e37/tests/mimc.rs#L147. + let vk_alpha_g1 = std::option::extract(&mut deserialize(&x"9819f632fa8d724e351d25081ea31ccf379991ac25c90666e07103fffb042ed91c76351cd5a24041b40e26d231a5087e")); + let vk_beta_g2 = std::option::extract(&mut deserialize(&x"871f36a996c71a89499ffe99aa7d3f94decdd2ca8b070dbb467e42d25aad918af6ec94d61b0b899c8f724b2b549d99fc1623a0e51b6cfbea220e70e7da5803c8ad1144a67f98934a6bf2881ec6407678fd52711466ad608d676c60319a299824")); + let vk_gamma_g2 = std::option::extract(&mut deserialize(&x"96750d8445596af8d679487c7267ae9734aeac584ace191d225680a18ecff8ebae6dd6a5fd68e4414b1611164904ee120363c2b49f33a873d6cfc26249b66327a0de03e673b8139f79809e8b641586cde9943fa072ee5ed701c81b3fd426c220")); + let vk_delta_g2 = std::option::extract(&mut deserialize(&x"8d3ac832f2508af6f01872ada87ea66d2fb5b099d34c5bac81e7482c956276dfc234c8d2af5fd2394b5440d0708a2c9f124a53c0755e9595cf9f8adade5deefcb8a574a67debd3b74d08c49c23ddc14cd6d48b65dce500c8a5d330e760fe85bb")); + let vk_gamma_abc_g1: vector> = vector[ + std::option::extract(&mut deserialize(&x"b0df760d0f2d67fdff69d0ed3a0653dd8808df3c407ea4d0e27f8612c3fbb748cb4372d33cac512ee5ef4ee1683c3fe5")), + std::option::extract(&mut deserialize(&x"96ec80d6b1050bbfc209f727678acce8788c05475771daffdd444ad8786c7a40195d859850fe2e72be3054e9fb8ce805")), + ]; + let public_inputs: vector> = vector[ + std::option::extract(&mut deserialize(&x"0ee291cfc951388c3c7f7c85ff2dfd42bbc66a6b4acaef9a5a51ce955125a74f")), + ]; + let proof_a = std::option::extract(&mut deserialize(&x"8a7c7364403d91bfa5c723ce93b920c8d2e559ea5e7e34eb68cea437aa4f26bf56ba22d9400988a86f2943c79401e959")); + let proof_b = std::option::extract(&mut deserialize(&x"9352f8a2f9ff60d390e363d063354e9728adf39c91294499575855e803dd80eeaa1488cd24d1b80eb1b2625011e22a5d139e24f2c7ac3508874ec4bdb9c71ddf109e7853d641d23ed27bef265248d78eabe9137c03b088d8adbdf39e10f87eab")); + let proof_c = std::option::extract(&mut deserialize(&x"896f68b438e076d3017e64aa47621fcd69b45f49a7038e2b1b9ed4f2de9b8eb8e0a76785a39a08f024435811a73a6818")); + + assert!(verify_proof( + &vk_alpha_g1, + &vk_beta_g2, + &vk_gamma_g2, + &vk_delta_g2, + &vk_gamma_abc_g1, + &public_inputs, + &proof_a, + &proof_b, + &proof_c, + ), 1); + } + + #[test(fx = @std)] + fun test_verify_proof_prepared_with_bls12381(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Below is an example MIMC proof sampled from test case https://github.com/arkworks-rs/groth16/blob/b6f9166bcf15ff4bfe101bb34e1bdc0d92302e37/tests/mimc.rs#L147. + let pvk_alpha_g1_beta_g2 = std::option::extract(&mut deserialize(&x"15cee98b42f8d158f421bce13983e23597123817a3b19b006294b9145f3f382686706ad9161d6234661fb1a32da19d0e2a9e672901fe4abe9efd4da96bcdb8324459b93aa48a8abb92ddd28ef053f118e190eddd6c6212bc09428ea05e709104290e37f320a3aac1dcf96f66efd9f5826b69cd075b72801ef54ccb740a0947bb3f73174e5d2fdc04292f58841ad9cc0d0c25021dfd8d592943b5e61c97f1ba68dcabd7de970ecc347c04bbaf9a062d9d49476f0b5bc77b2b9c7222781c53b713c0aae7a4cc57ff8cfb433d27fb1328d0c5453dbb97f3a70e9ce3b1da52cee2047cad225410b6dacb28e7b6876795d005cf0aefb7f25350d0197a5c2aa7369a5e06a210580bba1cc1941e1871a465cf68c84f32a29e6e898e4961a2b1fd5f8f03f03b1e1a0e191becdc8f01fb15adeb7cb6cc39e686edfcf7d65e952cf5e19a477fb5f6d2dab61a4d6c07777c1842150646c8b6fcb5989d9e524a97e7bf8b7be6b12983205970f16aeaccbdbe6cd565fa570dc45b0ad8f51c46e1f05e9f3f230dcf7567db5fc9a59a55c39139c7b357103c26bca9b70032cccff2345b76f596901ea81dc28f1d490a129501cf02204e00e8b59770188d69379144629239933523a8ec71ce6f91fbd01b2b9c411f89948183fea3949d89919e239a4aadb2347803e97ae8f7f20ade26da001f803cd61eb9bf8a67356f7cf6ec1744720b078eb992529f5c219bf16d5ef2e233a04572730e7c9572eadd9aa63c69c9f7dcf3423b1dc4c9b2032c8a7bbe91505283163a85413ecf0a0095fe1899b29f60011226f009")); + let pvk_gamma_g2_neg = std::option::extract(&mut deserialize(&x"b6750d8445596af8d679487c7267ae9734aeac584ace191d225680a18ecff8ebae6dd6a5fd68e4414b1611164904ee120363c2b49f33a873d6cfc26249b66327a0de03e673b8139f79809e8b641586cde9943fa072ee5ed701c81b3fd426c220")); + let pvk_delta_g2_neg = std::option::extract(&mut deserialize(&x"ad3ac832f2508af6f01872ada87ea66d2fb5b099d34c5bac81e7482c956276dfc234c8d2af5fd2394b5440d0708a2c9f124a53c0755e9595cf9f8adade5deefcb8a574a67debd3b74d08c49c23ddc14cd6d48b65dce500c8a5d330e760fe85bb")); + let pvk_gamma_abc_g1: vector> = vector[ + std::option::extract(&mut deserialize(&x"b0df760d0f2d67fdff69d0ed3a0653dd8808df3c407ea4d0e27f8612c3fbb748cb4372d33cac512ee5ef4ee1683c3fe5")), + std::option::extract(&mut deserialize(&x"96ec80d6b1050bbfc209f727678acce8788c05475771daffdd444ad8786c7a40195d859850fe2e72be3054e9fb8ce805")), + ]; + let public_inputs: vector> = vector[ + std::option::extract(&mut deserialize(&x"0ee291cfc951388c3c7f7c85ff2dfd42bbc66a6b4acaef9a5a51ce955125a74f")), + ]; + let proof_a = std::option::extract(&mut deserialize(&x"8a7c7364403d91bfa5c723ce93b920c8d2e559ea5e7e34eb68cea437aa4f26bf56ba22d9400988a86f2943c79401e959")); + let proof_b = std::option::extract(&mut deserialize(&x"9352f8a2f9ff60d390e363d063354e9728adf39c91294499575855e803dd80eeaa1488cd24d1b80eb1b2625011e22a5d139e24f2c7ac3508874ec4bdb9c71ddf109e7853d641d23ed27bef265248d78eabe9137c03b088d8adbdf39e10f87eab")); + let proof_c = std::option::extract(&mut deserialize(&x"896f68b438e076d3017e64aa47621fcd69b45f49a7038e2b1b9ed4f2de9b8eb8e0a76785a39a08f024435811a73a6818")); + + assert!(verify_proof_prepared( + &pvk_alpha_g1_beta_g2, + &pvk_gamma_g2_neg, + &pvk_delta_g2_neg, + &pvk_gamma_abc_g1, + &public_inputs, + &proof_a, + &proof_b, + &proof_c, + ), 1); + } + + #[test(fx = @std)] + fun test_verify_proof_prepared_fq12_with_bls12381(fx: signer) { + enable_cryptography_algebra_natives(&fx); + + // Below is an example MIMC proof sampled from test case https://github.com/arkworks-rs/groth16/blob/b6f9166bcf15ff4bfe101bb34e1bdc0d92302e37/tests/mimc.rs#L147. + let pvk_alpha_g1_beta_g2 = std::option::extract(&mut deserialize(&x"15cee98b42f8d158f421bce13983e23597123817a3b19b006294b9145f3f382686706ad9161d6234661fb1a32da19d0e2a9e672901fe4abe9efd4da96bcdb8324459b93aa48a8abb92ddd28ef053f118e190eddd6c6212bc09428ea05e709104290e37f320a3aac1dcf96f66efd9f5826b69cd075b72801ef54ccb740a0947bb3f73174e5d2fdc04292f58841ad9cc0d0c25021dfd8d592943b5e61c97f1ba68dcabd7de970ecc347c04bbaf9a062d9d49476f0b5bc77b2b9c7222781c53b713c0aae7a4cc57ff8cfb433d27fb1328d0c5453dbb97f3a70e9ce3b1da52cee2047cad225410b6dacb28e7b6876795d005cf0aefb7f25350d0197a5c2aa7369a5e06a210580bba1cc1941e1871a465cf68c84f32a29e6e898e4961a2b1fd5f8f03f03b1e1a0e191becdc8f01fb15adeb7cb6cc39e686edfcf7d65e952cf5e19a477fb5f6d2dab61a4d6c07777c1842150646c8b6fcb5989d9e524a97e7bf8b7be6b12983205970f16aeaccbdbe6cd565fa570dc45b0ad8f51c46e1f05e9f3f230dcf7567db5fc9a59a55c39139c7b357103c26bca9b70032cccff2345b76f596901ea81dc28f1d490a129501cf02204e00e8b59770188d69379144629239933523a8ec71ce6f91fbd01b2b9c411f89948183fea3949d89919e239a4aadb2347803e97ae8f7f20ade26da001f803cd61eb9bf8a67356f7cf6ec1744720b078eb992529f5c219bf16d5ef2e233a04572730e7c9572eadd9aa63c69c9f7dcf3423b1dc4c9b2032c8a7bbe91505283163a85413ecf0a0095fe1899b29f60011226f009")); + let pvk_gamma_g2_neg = std::option::extract(&mut deserialize(&x"b6750d8445596af8d679487c7267ae9734aeac584ace191d225680a18ecff8ebae6dd6a5fd68e4414b1611164904ee120363c2b49f33a873d6cfc26249b66327a0de03e673b8139f79809e8b641586cde9943fa072ee5ed701c81b3fd426c220")); + let pvk_delta_g2_neg = std::option::extract(&mut deserialize(&x"ad3ac832f2508af6f01872ada87ea66d2fb5b099d34c5bac81e7482c956276dfc234c8d2af5fd2394b5440d0708a2c9f124a53c0755e9595cf9f8adade5deefcb8a574a67debd3b74d08c49c23ddc14cd6d48b65dce500c8a5d330e760fe85bb")); + let pvk_gamma_abc_g1: vector> = vector[ + std::option::extract(&mut deserialize(&x"b0df760d0f2d67fdff69d0ed3a0653dd8808df3c407ea4d0e27f8612c3fbb748cb4372d33cac512ee5ef4ee1683c3fe5")), + std::option::extract(&mut deserialize(&x"96ec80d6b1050bbfc209f727678acce8788c05475771daffdd444ad8786c7a40195d859850fe2e72be3054e9fb8ce805")), + ]; + let public_inputs: vector> = vector[ + std::option::extract(&mut deserialize(&x"0ee291cfc951388c3c7f7c85ff2dfd42bbc66a6b4acaef9a5a51ce955125a74f")), + ]; + let proof_a = std::option::extract(&mut deserialize(&x"8a7c7364403d91bfa5c723ce93b920c8d2e559ea5e7e34eb68cea437aa4f26bf56ba22d9400988a86f2943c79401e959")); + let proof_b = std::option::extract(&mut deserialize(&x"9352f8a2f9ff60d390e363d063354e9728adf39c91294499575855e803dd80eeaa1488cd24d1b80eb1b2625011e22a5d139e24f2c7ac3508874ec4bdb9c71ddf109e7853d641d23ed27bef265248d78eabe9137c03b088d8adbdf39e10f87eab")); + let proof_c = std::option::extract(&mut deserialize(&x"896f68b438e076d3017e64aa47621fcd69b45f49a7038e2b1b9ed4f2de9b8eb8e0a76785a39a08f024435811a73a6818")); + + assert!(verify_proof_prepared_fq12( + &pvk_alpha_g1_beta_g2, + &pvk_gamma_g2_neg, + &pvk_delta_g2_neg, + &pvk_gamma_abc_g1, + &public_inputs, + &proof_a, + &proof_b, + &proof_c, + ), 1); + } +} diff --git a/aptos-move/move-examples/tests/move_unit_tests.rs b/aptos-move/move-examples/tests/move_unit_tests.rs index 334624b40941e..d9b0f6c10b175 100644 --- a/aptos-move/move-examples/tests/move_unit_tests.rs +++ b/aptos-move/move-examples/tests/move_unit_tests.rs @@ -85,6 +85,11 @@ fn test_defi() { test_common("defi"); } +#[test] +fn test_groth16() { + test_common("groth16_example"); +} + #[test] fn test_hello_blockchain() { test_common("hello_blockchain"); diff --git a/crates/aptos-crypto/Cargo.toml b/crates/aptos-crypto/Cargo.toml index dadcf96be2f99..3d1d76356181b 100644 --- a/crates/aptos-crypto/Cargo.toml +++ b/crates/aptos-crypto/Cargo.toml @@ -15,6 +15,9 @@ rust-version = { workspace = true } [dependencies] anyhow = { workspace = true } aptos-crypto-derive = { workspace = true } +ark-ec = { workspace = true } +ark-ff = { workspace = true } +ark-std = { workspace = true } bcs = { workspace = true } blst = { workspace = true } bytes = { workspace = true } @@ -35,6 +38,7 @@ serde = { workspace = true } serde-name = { workspace = true } serde_bytes = { workspace = true } sha2 = { workspace = true } +sha2_0_10_6 = { workspace = true } static_assertions = { workspace = true } thiserror = { workspace = true } tiny-keccak = { workspace = true } @@ -42,8 +46,6 @@ x25519-dalek = { workspace = true } [dev-dependencies] ark-bls12-381 = { workspace = true } -ark-ec = { workspace = true } -ark-ff = { workspace = true } ark-serialize = { workspace = true } ark-std = { workspace = true } bitvec = { workspace = true } @@ -63,6 +65,10 @@ assert-private-keys-not-cloneable = [] cloneable-private-keys = [] fuzzing = ["proptest", "proptest-derive", "cloneable-private-keys"] +[[bench]] +name = "ark_bls12_381" +harness = false + [[bench]] name = "bls12381" harness = false @@ -86,3 +92,4 @@ harness = false [[bench]] name = "secp256k1" harness = false + diff --git a/crates/aptos-crypto/benches/ark_bls12_381.rs b/crates/aptos-crypto/benches/ark_bls12_381.rs new file mode 100644 index 0000000000000..341376dca48ea --- /dev/null +++ b/crates/aptos-crypto/benches/ark_bls12_381.rs @@ -0,0 +1,896 @@ +// Copyright © Aptos Foundation + +// Copyright (c) Aptos +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::test_utils::random_bytes; +use ark_bls12_381::{Fq12, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_ec::{ + hashing::HashToCurve, pairing::Pairing, short_weierstrass::Projective, AffineRepr, CurveGroup, + Group, +}; +use ark_ff::{BigInteger256, Field, One, UniformRand, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::test_rng; +use criterion::{BenchmarkId, Criterion}; +use rand::thread_rng; +use std::ops::{Add, Div, Mul, Neg}; + +fn msm_all_bench_cases() -> Vec { + let series_until_65 = (1..65).step_by(2); + let series_until_129 = (64..129).step_by(4); + let series_until_257 = (129..257).step_by(8); + series_until_65 + .chain(series_until_129) + .chain(series_until_257) + .collect::>() +} + +macro_rules! rand { + ($typ:ty) => {{ + <$typ>::rand(&mut test_rng()) + }}; +} + +macro_rules! serialize { + ($obj:expr, $method:ident) => {{ + let mut buf = vec![]; + $obj.$method(&mut buf).unwrap(); + buf + }}; +} + +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("ark_bls12_381"); + + group.bench_function("fr_add", move |b| { + b.iter_with_setup( + || (rand!(Fr), rand!(Fr)), + |(k_1, k_2)| { + let _k_3 = k_1 + k_2; + }, + ) + }); + + group.bench_function("fr_deser", move |b| { + b.iter_with_setup( + || { + let k = rand!(Fr); + serialize!(k, serialize_uncompressed) + }, + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()).unwrap(); + }, + ) + }); + + group.bench_function("fr_deser_invalid_4_bytes", move |b| { + b.iter_with_setup( + || vec![0xFF_u8; 4], + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("fr_deser_invalid_4000_bytes", move |b| { + b.iter_with_setup( + || vec![0xFF_u8; 4000], + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("fr_deser_invalid_4000000_bytes", move |b| { + b.iter_with_setup( + || vec![0xFF_u8; 4000000], + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("fr_div", move |b| { + b.iter_with_setup( + || (rand!(Fr), rand!(Fr)), + |(k_1, k_2)| { + let _k_3 = k_1 / k_2; + }, + ) + }); + + group.bench_function("fr_eq", move |b| { + b.iter_with_setup( + || { + let k_1 = rand!(Fr); + let k_2 = k_1; + (k_1, k_2) + }, + |(k_1, k_2)| { + let _res = k_1 == k_2; + }, + ) + }); + + group.bench_function("fr_from_u64", move |b| { + b.iter_with_setup( + || rand!(u64), + |v| { + let _res: Fr = BigInteger256::from(v).into(); + }, + ) + }); + + group.bench_function("fr_inv", move |b| { + b.iter_with_setup( + || rand!(Fr), + |k| { + let _k_inv = k.inverse(); + }, + ) + }); + + group.bench_function("fr_mul", move |b| { + b.iter_with_setup( + || (rand!(Fr), rand!(Fr)), + |(k_1, k_2)| { + let _k_3 = k_1 * k_2; + }, + ) + }); + + group.bench_function("fr_mul_self", move |b| { + b.iter_with_setup( + || rand!(Fr), + |k| { + let _k2 = k.mul(&k); + }, + ) + }); + + group.bench_function("fr_neg", move |b| { + b.iter_with_setup( + || rand!(Fr), + |k| { + let _k_inv = k.neg(); + }, + ) + }); + + group.bench_function("fr_one", move |b| { + b.iter_with_setup( + || {}, + |_| { + let _k = Fr::one(); + }, + ) + }); + + group.bench_function("fr_serialize", move |b| { + b.iter_with_setup( + || rand!(Fr), + |k| { + let _buf = serialize!(k, serialize_uncompressed); + }, + ) + }); + + group.bench_function("fr_square", move |b| { + b.iter_with_setup( + || rand!(Fr), + |k| { + let _k2 = k.square(); + }, + ) + }); + + group.bench_function("fr_sub", move |b| { + b.iter_with_setup( + || (rand!(Fr), rand!(Fr)), + |(k_1, k_2)| { + let _k_3 = k_1 - k_2; + }, + ) + }); + + group.bench_function("fr_zero", move |b| { + b.iter_with_setup( + || {}, + |_| { + let _k = Fr::zero(); + }, + ) + }); + + group.bench_function("fq12_add", move |b| { + b.iter_with_setup( + || (rand!(Fq12), rand!(Fq12)), + |(e_1, e_2)| { + let _e_3 = e_1 + e_2; + }, + ) + }); + + group.bench_function("fq12_add_self", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e.add(&e); + }, + ) + }); + + group.bench_function("fq12_clone", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e; + }, + ) + }); + + group.bench_function("fq12_deser", move |b| { + b.iter_with_setup( + || { + let e = rand!(Fq12); + serialize!(e, serialize_uncompressed) + }, + |buf| { + let _e = Fq12::deserialize_uncompressed(buf.as_slice()).unwrap(); + }, + ) + }); + + group.bench_function("fq12_div", move |b| { + b.iter_with_setup( + || { + let e = rand!(Fq12); + let f = rand!(Fq12); + (e, f) + }, + |(e, f)| { + let _g = e.div(f); + }, + ) + }); + + group.bench_function("fq12_double", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e.double(); + }, + ) + }); + + group.bench_function("fq12_eq", move |b| { + b.iter_with_setup( + || { + let e_1 = rand!(Fq12); + let e_2 = e_1; + (e_1, e_2) + }, + |(e_1, e_2)| { + let _res = e_1 == e_2; + }, + ) + }); + + group.bench_function("fq12_from_u64", move |b| { + b.iter_with_setup( + || rand!(u64), + |i| { + let _res = Fq12::from(i); + }, + ) + }); + + group.bench_function("fq12_inv", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_inv = e.inverse(); + }, + ) + }); + + group.bench_function("fq12_mul", move |b| { + b.iter_with_setup( + || (rand!(Fq12), rand!(Fq12)), + |(e_1, e_2)| { + let _e_3 = e_1 * e_2; + }, + ) + }); + + group.bench_function("fq12_mul_self", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e.mul(&e); + }, + ) + }); + + group.bench_function("fq12_neg", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e.neg(); + }, + ) + }); + + group.bench_function("fq12_one", move |b| { + b.iter(|| { + let _e = Fq12::one(); + }) + }); + + group.bench_function("fq12_pow_u256", move |b| { + b.iter_with_setup( + || { + let base = rand!(Fq12); + let exp = rand!(Fr); + let exp = BigInteger256::from(exp); + (base, exp) + }, + |(base, exp)| { + let _res = base.pow(exp); + }, + ) + }); + + group.bench_function("fq12_serialize", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let mut buf = vec![]; + e.serialize_uncompressed(&mut buf).unwrap(); + }, + ) + }); + + group.bench_function("fq12_square", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _res = e.square(); + }, + ) + }); + + group.bench_function("fq12_sub", move |b| { + b.iter_with_setup( + || (rand!(Fq12), rand!(Fq12)), + |(e, f)| { + let _res = e - f; + }, + ) + }); + + group.bench_function("fq12_zero", move |b| { + b.iter_with_setup( + || (), + |_| { + let _res = Fq12::zero(); + }, + ) + }); + + group.bench_function("g1_affine_add", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(G1Affine)), + |(p1, p2)| { + let _p3 = p1 + p2; + }, + ) + }); + + group.bench_function("g1_affine_deser_comp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Affine); + serialize!(p, serialize_compressed) + }, + |buf| { + let _p = G1Affine::deserialize_compressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g1_affine_deser_uncomp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Affine); + serialize!(p, serialize_uncompressed) + }, + |buf| { + let _p = G1Affine::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g1_affine_eq", move |b| { + b.iter_with_setup( + || { + let p1 = rand!(G1Affine); + let p2 = p1; + (p1, p2) + }, + |(p1, p2)| { + let _res = p1 == Projective::from(p2); + }, + ) + }); + + group.bench_function("g1_affine_generator", move |b| { + b.iter(|| { + let _res = G1Affine::generator(); + }) + }); + + group.bench_function("g1_affine_infinity", move |b| { + b.iter(|| { + let _res = G1Affine::zero(); + }) + }); + + group.bench_function("g1_affine_scalar_mul_to_proj", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(Fr)), + |(p, k)| { + let _res = p.mul(k); + }, + ) + }); + + group.bench_function("g1_affine_neg", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p| { + let _res = p.neg(); + }, + ) + }); + + group.bench_function("g1_affine_serialize_comp", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_compressed); + }, + ) + }); + + group.bench_function("g1_affine_serialize_uncomp", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_uncompressed); + }, + ) + }); + + group.bench_function("g1_affine_to_proj", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _res = G1Projective::from(p_affine); + }, + ) + }); + + group.bench_function("g1_proj_add", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = rand!(G1Projective); + (p, q) + }, + |(p, q)| { + let _res = p + q; + }, + ) + }); + + group.bench_function("g1_proj_double", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p| { + let _q = p.double(); + }, + ) + }); + + group.bench_function("g1_proj_eq", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = p; + (p, q) + }, + |(p, q)| { + let _res = p == q; + }, + ) + }); + + group.bench_function("g1_proj_generator", move |b| { + b.iter(|| { + let _res = G1Projective::generator(); + }) + }); + + group.bench_function("g1_proj_infinity", move |b| { + b.iter(|| { + let _res = G1Projective::zero(); + }) + }); + + group.bench_function("g1_proj_neg", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p| { + let _q = p.neg(); + }, + ) + }); + + group.bench_function("g1_proj_scalar_mul", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let k = rand!(Fr); + (p, k) + }, + |(p, k)| { + let _q = p.mul(k); + }, + ) + }); + + group.bench_function("g1_proj_sub", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = rand!(G1Projective); + (p, q) + }, + |(p, q)| { + let _r = p - q; + }, + ) + }); + + group.bench_function("g1_proj_to_affine", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p_proj| { + let _ = p_proj.into_affine(); + }, + ) + }); + + group.bench_function("g2_affine_add", move |b| { + b.iter_with_setup( + || (rand!(G2Affine), rand!(G2Affine)), + |(p1, p2)| { + let _p3 = p1 + p2; + }, + ) + }); + + group.bench_function("g2_affine_deser_comp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Affine); + serialize!(p, serialize_compressed) + }, + |buf| { + let _p = G2Affine::deserialize_compressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g2_affine_deser_uncomp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Affine); + serialize!(p, serialize_uncompressed) + }, + |buf| { + let _p = G2Affine::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g2_affine_eq", move |b| { + b.iter_with_setup( + || { + let p1 = rand!(G2Affine); + let p2 = p1; + (p1, p2) + }, + |(p1, p2)| { + let _res = p1 == Projective::from(p2); + }, + ) + }); + + group.bench_function("g2_affine_generator", move |b| { + b.iter(|| { + let _res = G2Affine::generator(); + }) + }); + + group.bench_function("g2_affine_infinity", move |b| { + b.iter(|| { + let _res = G2Affine::zero(); + }) + }); + + group.bench_function("g2_affine_scalar_mul_to_proj", move |b| { + b.iter_with_setup( + || (rand!(G2Affine), rand!(Fr)), + |(p, k)| { + let _res = p.mul(k); + }, + ) + }); + + group.bench_function("g2_affine_neg", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p| { + let _res = p.neg(); + }, + ) + }); + + group.bench_function("g2_affine_serialize_comp", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_compressed); + }, + ) + }); + + group.bench_function("g2_affine_serialize_uncomp", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_uncompressed); + }, + ) + }); + + group.bench_function("g2_affine_to_proj", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _res = G2Projective::from(p_affine); + }, + ) + }); + + group.bench_function("g2_proj_add", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = rand!(G2Projective); + (p, q) + }, + |(p, q)| { + let _res = p + q; + }, + ) + }); + + group.bench_function("g2_proj_double", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p| { + let _q = p.double(); + }, + ) + }); + + group.bench_function("g2_proj_eq", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = p; + (p, q) + }, + |(p, q)| { + let _res = p == q; + }, + ) + }); + + group.bench_function("g2_proj_generator", move |b| { + b.iter(|| { + let _res = G2Projective::generator(); + }) + }); + + group.bench_function("g2_proj_infinity", move |b| { + b.iter(|| { + let _res = G2Projective::zero(); + }) + }); + + group.bench_function("g2_proj_neg", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p| { + let _q = p.neg(); + }, + ) + }); + + group.bench_function("g2_proj_scalar_mul", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let k = rand!(Fr); + (p, k) + }, + |(p, k)| { + let _q = p.mul(k); + }, + ) + }); + + group.bench_function("g2_proj_sub", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = rand!(G2Projective); + (p, q) + }, + |(p, q)| { + let _r = p - q; + }, + ) + }); + + group.bench_function("g2_proj_to_affine", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p_proj| { + let _ = p_proj.into_affine(); + }, + ) + }); + + group.bench_function("pairing", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(G2Affine)), + |(g1e, g2e)| { + let _res = ark_bls12_381::Bls12_381::pairing(g1e, g2e).0; + }, + ) + }); + + let linear_regression_max_num_datapoints = 20; + + let pairing_product_max_num_pairs = 100; + for num_pairs in (0..pairing_product_max_num_pairs) + .step_by(pairing_product_max_num_pairs / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("pairing_product", num_pairs), |b| { + b.iter_with_setup( + || { + let g1_elements = (0..num_pairs).map(|_i| rand!(G1Affine)).collect::>(); + let g2_elements = (0..num_pairs).map(|_i| rand!(G2Affine)).collect::>(); + (g1_elements, g2_elements) + }, + |(g1_elements, g2_elements)| { + let _product = + ark_bls12_381::Bls12_381::multi_pairing(g1_elements, g2_elements).0; + }, + ); + }); + } + + for num_entries in msm_all_bench_cases() { + group.bench_function(BenchmarkId::new("g1_affine_msm", num_entries), |b| { + b.iter_with_setup( + || { + let elements = (0..num_entries) + .map(|_i| rand!(G1Affine)) + .collect::>(); + let scalars = (0..num_entries).map(|_i| rand!(Fr)).collect::>(); + (elements, scalars) + }, + |(elements, scalars)| { + let _res: G1Projective = + ark_ec::VariableBaseMSM::msm(elements.as_slice(), scalars.as_slice()) + .unwrap(); + }, + ); + }); + } + + for num_entries in msm_all_bench_cases() { + group.bench_function(BenchmarkId::new("g2_affine_msm", num_entries), |b| { + b.iter_with_setup( + || { + let elements = (0..num_entries) + .map(|_i| rand!(G2Affine)) + .collect::>(); + let scalars = (0..num_entries).map(|_i| rand!(Fr)).collect::>(); + (elements, scalars) + }, + |(elements, scalars)| { + let _res: G2Projective = + ark_ec::VariableBaseMSM::msm(elements.as_slice(), scalars.as_slice()) + .unwrap(); + }, + ); + }); + } + + let hash_to_curve_max_msg_len = 1048576; + + for msg_len in (0..hash_to_curve_max_msg_len) + .step_by(hash_to_curve_max_msg_len / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("hash_to_g1_proj", msg_len), |b| { + b.iter_with_setup( + || { + let dst = random_bytes(&mut thread_rng(), 255); + let msg = random_bytes(&mut thread_rng(), msg_len); + (dst, msg) + }, + |(dst, msg)| { + let mapper = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + Projective, + ark_ff::fields::field_hashers::DefaultFieldHasher, + ark_ec::hashing::curve_maps::wb::WBMap, + >::new(dst.as_slice()) + .unwrap(); + let _new_element = ::from(mapper.hash(msg.as_slice()).unwrap()); + }, + ); + }); + } + + for msg_len in (0..hash_to_curve_max_msg_len) + .step_by(hash_to_curve_max_msg_len / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("hash_to_g2_proj", msg_len), |b| { + b.iter_with_setup( + || { + let dst = random_bytes(&mut thread_rng(), 255); + let msg = random_bytes(&mut thread_rng(), msg_len); + (dst, msg) + }, + |(dst, msg)| { + let mapper = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + Projective, + ark_ff::fields::field_hashers::DefaultFieldHasher, + ark_ec::hashing::curve_maps::wb::WBMap, + >::new(dst.as_slice()) + .unwrap(); + let _new_element = ::from(mapper.hash(msg.as_slice()).unwrap()); + }, + ); + }); + } + + group.finish(); +} + +criterion_group!( + name = ark_bls12_381_benches; + config = Criterion::default(); //.measurement_time(Duration::from_secs(100)); + targets = bench_group); +criterion_main!(ark_bls12_381_benches); diff --git a/scripts/algebra-gas/README.md b/scripts/algebra-gas/README.md new file mode 100644 index 0000000000000..77388fa7db306 --- /dev/null +++ b/scripts/algebra-gas/README.md @@ -0,0 +1,43 @@ +Scripts that help generate/evaluate gas parameters for generic algebra move module. + +## Quickstart guide +Ensure you are on a machine with the [required spec](https://aptos.dev/nodes/validator-node/operator/node-requirements/). + +Ensure you have python3 and the following dependencies. +``` +pip3 install numpy matplotlib +``` + +Ensure you `cd` to the repo root. + +Run the necessary benches. +``` +cargo bench -p aptos-crypto -- hash/SHA2-256 +cargo bench -p aptos-crypto -- ark_bls12_381 +``` + +Compute `gas_per_ns` using `hash/SHA2-256` bench results. +``` +scripts/algebra-gas/load_bench_datapoints.py --bench_path target/criterion/hash/SHA2-256 +scripts/algebra-gas/fit_linear_model.py --dataset_path hash_SHA2_256.0-1025.json --plot +``` +This will fit a curve `f(n)=kn+b` +that predicts the time (in nanoseconds) to evaluate SHA2-256 on an input of size `n`. +Value `k` and `b` should be printed. +``` +{"b": 336.51096106242346, "k": 4.868293006038344} +``` + +Combined with the [pre-defined](https://github.com/aptos-labs/aptos-core/blob/28df4c1f0ea0d6c6dc6b0460257aa9086e830d1a/aptos-move/aptos-gas/src/move_stdlib.rs#L17-L18) SHA2-256 gas formula (unscaled internal gas):`g(n)=50n+3000`, +it can be calculated that `gas_per_ns = 50/k`. + +Second last, go to `scripts/algebra-gas/update_algebra_gas_params.py` +and update the value of the global variable `TARGET_GAS_VERSION` if necessary. +See the comments on them for detailed instructions. + +Now you can (re-)generate all algebra module gas parameters with one command. +``` +scripts/algebra-gas/update_algebra_gas_params.py --gas_per_ns +``` + +`git diff` to see the diff! diff --git a/scripts/algebra-gas/fit_linear_model.py b/scripts/algebra-gas/fit_linear_model.py new file mode 100755 index 0000000000000..bf5496612e3f5 --- /dev/null +++ b/scripts/algebra-gas/fit_linear_model.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import argparse +import json +import matplotlib.pyplot as plt +import numpy as np +from pathlib import Path +from time import time + +def main(datapoints:list): + '''Least-squares fit a line to a given dataset.''' + for item in datapoints: assert len(item)==2 + x_values, y_values = zip(*datapoints) + X = np.array(x_values) + Y = np.array(y_values) + A = np.vstack([X, np.ones(len(X))]).T + sol, residuals, _, _ = np.linalg.lstsq(A, Y, rcond=None) + k,b = sol + return (X, Y, k, b) + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--dataset_path', required=True) + parser.add_argument('--plot', action='store_true') + args = parser.parse_args() + jsonstr = Path(args.dataset_path).read_text() + datapoints = json.loads(jsonstr) + X,Y,k,b = main(datapoints) + jsonstr = json.dumps({'b':b, 'k':k}) + print(jsonstr) + print() + dataset_base_path = args.dataset_path.replace('.json', '') + cur_time = int(time()) + out_path = Path(f'{dataset_base_path}.model.{cur_time}.json') + print(f'Saving model to:') + print() + print(f' {out_path}') + print() + out_path.write_text(jsonstr) + if args.plot: + plt.plot(X, Y, 'o', label='dataset', markersize=2) + plt.plot(X, k*X+b, 'r', label='fitted') + plt.legend() + plt.show(block=True) diff --git a/scripts/algebra-gas/load_bench_datapoints.py b/scripts/algebra-gas/load_bench_datapoints.py new file mode 100755 index 0000000000000..0c01cb1c253cc --- /dev/null +++ b/scripts/algebra-gas/load_bench_datapoints.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import argparse +import load_bench_ns +from glob import glob +import json +import matplotlib.pyplot as plt +import numpy as np +from pathlib import Path +import re +from time import time + +def get_datapoint(bench_path): + items = bench_path.split('/') + if not items[-1].isdecimal(): return None + arg = int(items[-1]) + ns = load_bench_ns.main(bench_path) + return (arg,ns) + +def main(bench_path): + '''Parse benchmark results as datapoints. + + Param `bench_path` has to be a serial bench, (e.g. 'target/criterion/hash/SHA2-256'). + ''' + datapoints = [get_datapoint(sub_bench_path) for sub_bench_path in glob(f'{bench_path}/*')] + datapoints = [dp for dp in datapoints if dp!=None] + assert len(datapoints)>=1 + datapoints.sort() + return datapoints + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--bench_path', required=True) + parser.add_argument('--plot', action='store_true') + args = parser.parse_args() + datapoints = main(args.bench_path) + jsonstr = json.dumps(datapoints) + print(jsonstr) + print() + # Save to file. + bench_name = str(Path(args.bench_path).relative_to(Path('target/criterion'))) + formatted_bench_name = re.sub('[^0-9a-zA-Z]', '_', bench_name) + x_min = datapoints[0][0] + x_max = datapoints[-1][0] + cur_time = int(time()) + out_path = Path(f'{formatted_bench_name}.{cur_time}.{x_min}-{x_max+1}.json') + print(f'Saving dataset to:') + print() + print(f' {out_path}') + print() + out_path.write_text(jsonstr) + if args.plot: + x_values, y_values = zip(*datapoints) + X = np.array(x_values) + Y = np.array(y_values) + plt.plot(X, Y, 'o', label='ns sampled', markersize=2) + plt.legend() + plt.show(block=True) diff --git a/scripts/algebra-gas/load_bench_ns.py b/scripts/algebra-gas/load_bench_ns.py new file mode 100755 index 0000000000000..76a21335d6313 --- /dev/null +++ b/scripts/algebra-gas/load_bench_ns.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import argparse +import json +from pathlib import Path + +def main(bench_path): + '''Parse a value from a given benchmark result. + + Param `bench_path` can be the result of any single-datapoint bench (e.g., 'target/criterion/ark_bls12_381/fr_add'), + or a single result of a serial bench (e.g., 'target/criterion/hash/SHA2-256/0'). + ''' + json_path = Path(f'{bench_path}/new/estimates.json') + if not json_path.exists(): return None + jsonstr = json_path.read_text() + obj = json.loads(jsonstr) + ns = obj['median']['point_estimate'] + assert type(ns)==float + return ns + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--bench_path', required=True) + args = parser.parse_args() + ns = main(args.bench_path) + print(json.dumps({'ns':ns})) diff --git a/scripts/algebra-gas/plot_datasets.py b/scripts/algebra-gas/plot_datasets.py new file mode 100755 index 0000000000000..5fbf85b08d54d --- /dev/null +++ b/scripts/algebra-gas/plot_datasets.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +''' +This module plots an existing dataset. +''' + +import argparse +import json +import matplotlib.pyplot as plt +import numpy as np +from pathlib import Path + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('dataset_paths', nargs='+') + args = parser.parse_args() + for dataset_path in args.dataset_paths: + dataset = json.loads(Path(dataset_path).read_text()) + x_values, y_values = zip(*dataset) + X = np.array(x_values) + Y = np.array(y_values) + plt.plot(X, Y, 'o', label=dataset_path, markersize=2) + plt.legend() + plt.show(block=True) diff --git a/scripts/algebra-gas/score_model.py b/scripts/algebra-gas/score_model.py new file mode 100755 index 0000000000000..474799404c447 --- /dev/null +++ b/scripts/algebra-gas/score_model.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import argparse +import json +import load_bench_datapoints +import load_bench_ns +from math import ceil, log2 +import matplotlib.pyplot as plt +import numpy as np +from pathlib import Path +from pprint import pprint + +class ArkMsmModel: + def __init__(self, scalar_field_bitlen, addition_cost, doubling_cost): + self.scalar_field_bitlen = scalar_field_bitlen + self.addition_cost = addition_cost + self.doubling_cost = doubling_cost + def predict(self, x): + window_size = 3 if x < 32 else (ceil(log2(x)) * 69 // 100 + 2) + num_windows = ceil(self.scalar_field_bitlen / window_size) + num_buckets = 1 << window_size + cost = self.addition_cost * (x+num_buckets+1) * num_windows + self.doubling_cost * (window_size*num_windows) + return cost + +class LinearModel: + def __init__(self, k, b): + self.k = k + self.b = b + def predict(self, x): + return self.k*x+self.b + +def load_model(model_path): + if model_path == 'builtin_ark_bls12_381_g1_affine_msm': + return ArkMsmModel(255, load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_add'), load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_double')) + if model_path == 'builtin_ark_bls12_381_g2_affine_msm': + return ArkMsmModel(255, load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_add'), load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_double')) + obj = json.loads(Path(model_path).read_text()) + return LinearModel(obj['k'], obj['b']) + +class PointStat: + def __init__(self, x, y, y_hat): + self.x = x + self.y = y + self.y_hat = y_hat + self.est_rate = y_hat / y + def __repr__(self): + return f'x={self.x}, y={self.y}, y_hat={self.y_hat}, est_rate={self.est_rate}' + +def main(dataset_path, model_path): + '''Not actually scoring. Simply compare a dataset and the values predicted by a model, get the estimated/actual rate on every x and return a sorted list. + + Param `model_path` can be a file generated by `fit_linear_model.py`, or a hardcoded one specified in function `load_model()` in this file. + ''' + datapoints = json.loads(Path(dataset_path).read_text()) + x_values, y_values = zip(*datapoints) + model = load_model(model_path) + y_hat_values = [model.predict(x) for x in x_values] + X = np.array(x_values) + Y = np.array(y_values) + Y_hat = np.array(y_hat_values) + n = len(X) + stats = [PointStat(X[i], Y[i], Y_hat[i]) for i in range(n)] + stats.sort(key=lambda st:st.est_rate) + return X, Y, Y_hat, stats + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--dataset_path', required=True) + parser.add_argument('--model_path', required=True) + parser.add_argument('--plot', action='store_true') + args = parser.parse_args() + X, Y, Y_hat, stats_sorted_by_est_rate = main(args.dataset_path, args.model_path) + pprint(stats_sorted_by_est_rate) + if args.plot: + plt.plot(X, Y, 'o', label='ns sampled', markersize=2) + plt.plot(X, Y_hat, 'r', label='ns predicted') + plt.legend() + plt.show(block=True) diff --git a/scripts/algebra-gas/split_dataset.py b/scripts/algebra-gas/split_dataset.py new file mode 100755 index 0000000000000..3c74ab5feebbd --- /dev/null +++ b/scripts/algebra-gas/split_dataset.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +import argparse +import json +from pathlib import Path +import re +from time import time +import utils + +def main(sorted_datapoints, cut_point): + '''Split a dataset (sorted list of (x,y)) into 2, cutting at `x=cut_point`.''' + n = len(sorted_datapoints) + i = 0 + while i < n and sorted_datapoints[i][0] < cut_point: i+=1 + return sorted_datapoints[:i], sorted_datapoints[i:] + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--dataset_path', required=True) + parser.add_argument('--cut_point', required=True, type=int) + args = parser.parse_args() + base_path, x_begin, x_end = utils.parse_range_from_dataset_path(args.dataset_path) + assert x_begin <= args.cut_point < x_end + dataset = json.loads(Path(args.dataset_path).read_text()) + sub_dataset_left, sub_dataset_right = main(dataset, args.cut_point) + print(json.dumps({'left':sub_dataset_left, 'right':sub_dataset_right})) + + # Save to files. + path_to_left = Path(f'{base_path}.{x_begin}-{args.cut_point}.json') + path_to_right = Path(f'{base_path}.{args.cut_point}-{x_end}.json') + print(f'Saving datasets to:') + print() + print(f' {path_to_left}') + print(f' {path_to_right}') + print() + path_to_left.write_text(json.dumps(sub_dataset_left)) + path_to_right.write_text(json.dumps(sub_dataset_right)) diff --git a/scripts/algebra-gas/union_datasets.py b/scripts/algebra-gas/union_datasets.py new file mode 100755 index 0000000000000..d5788a0ed6458 --- /dev/null +++ b/scripts/algebra-gas/union_datasets.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import argparse +import json +from pathlib import Path +import utils + +def main(datasets): + '''Union a list of datasets (each is a list of (x,y)). + + If multiple datapoints are present on the same x, take the minimum. + ''' + best = {} + for dataset in datasets: + for x,y in dataset: + best[x] = min(y, best[x]) if x in best else y + new_dataset = [(k,v) for k,v in best.items()] + new_dataset.sort() + return new_dataset + +if __name__=='__main__': + parser = argparse.ArgumentParser() + parser.add_argument('dataset_paths', nargs='+') + args = parser.parse_args() + datasets = [json.loads(Path(path).read_text()) for path in args.dataset_paths] + base_path,_,_ = utils.parse_range_from_dataset_path(args.dataset_paths[0]) + new_dataset = main(datasets) + jsonstr = json.dumps(new_dataset) + print(jsonstr) + print() + # Save to file. + x_min = new_dataset[0][0] + x_max = new_dataset[-1][0] + out_path = Path(f'{base_path}.{x_min}-{x_max+1}.json') + print(f'Saving dataset to:') + print() + print(f' {out_path}') + print() + out_path.write_text(jsonstr) diff --git a/scripts/algebra-gas/update_algebra_gas_params.py b/scripts/algebra-gas/update_algebra_gas_params.py new file mode 100755 index 0000000000000..2eb69aa21cfb4 --- /dev/null +++ b/scripts/algebra-gas/update_algebra_gas_params.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +''' +This module automated the steps to +calculate gas parameters for `algebra.move` natives from benchmarking results, +then update the gas parameter definitions in rust. +''' + +import argparse +import fit_linear_model +import load_bench_ns +import load_bench_datapoints +from math import ceil +from pathlib import Path +from time import time + +# Typically you are making a new version of gas schedule, +# so this should be larger than `LATEST_GAS_FEATURE_VERSION` in `aptos-move/aptos-gas/src/gas_meter.rs`. +TARGET_GAS_VERSION = 8 + +def get_bench_ns_linear(bench_path): + datapoints = load_bench_datapoints.main(bench_path) + X,Y,k,b = fit_linear_model.main(datapoints) + return X,Y,k,b + +def prettify_number(x:int) -> str: + s = str(x) + n = len(s) + b = n % 3 + chunks_0 = [s[:b]] if b>=1 else [] + chunks = chunks_0 + [s[i:i+3] for i in range(b,n,3)] + return '_'.join(chunks) + +def get_algebra_lines(gas_per_ns): + nanoseconds = {} + nanoseconds['ark_bls12_381_fr_add'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_add') + nanoseconds['ark_bls12_381_fr_deser'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_deser') + nanoseconds['ark_bls12_381_fr_div'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_div') + nanoseconds['ark_bls12_381_fr_eq'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_eq') + nanoseconds['ark_bls12_381_fr_from_u64'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_from_u64') + nanoseconds['ark_bls12_381_fr_inv'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_inv') + nanoseconds['ark_bls12_381_fr_mul'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_mul') + nanoseconds['ark_bls12_381_fr_neg'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_neg') + nanoseconds['ark_bls12_381_fr_one'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_one') + nanoseconds['ark_bls12_381_fr_serialize'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_serialize') + nanoseconds['ark_bls12_381_fr_square'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_square') + nanoseconds['ark_bls12_381_fr_sub'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_sub') + nanoseconds['ark_bls12_381_fr_zero'] = load_bench_ns.main('target/criterion/ark_bls12_381/fr_zero') + nanoseconds['ark_bls12_381_fq12_add'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_add') + nanoseconds['ark_bls12_381_fq12_clone'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_clone') + nanoseconds['ark_bls12_381_fq12_deser'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_deser') + nanoseconds['ark_bls12_381_fq12_div'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_div') + nanoseconds['ark_bls12_381_fq12_eq'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_eq') + nanoseconds['ark_bls12_381_fq12_from_u64'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_from_u64') + nanoseconds['ark_bls12_381_fq12_inv'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_inv') + nanoseconds['ark_bls12_381_fq12_mul'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_mul') + nanoseconds['ark_bls12_381_fq12_neg'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_neg') + nanoseconds['ark_bls12_381_fq12_one'] = 1 + nanoseconds['ark_bls12_381_fq12_pow_u256'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_pow_u256') + nanoseconds['ark_bls12_381_fq12_serialize'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_serialize') + nanoseconds['ark_bls12_381_fq12_square'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_square') + nanoseconds['ark_bls12_381_fq12_sub'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_sub') + nanoseconds['ark_bls12_381_fq12_zero'] = load_bench_ns.main('target/criterion/ark_bls12_381/fq12_zero') + nanoseconds['ark_bls12_381_g1_affine_deser_comp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_affine_deser_comp') + nanoseconds['ark_bls12_381_g1_affine_deser_uncomp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_affine_deser_uncomp') + nanoseconds['ark_bls12_381_g1_affine_serialize_comp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_affine_serialize_comp') + nanoseconds['ark_bls12_381_g1_affine_serialize_uncomp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_affine_serialize_uncomp') + nanoseconds['ark_bls12_381_g1_proj_add'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_add') + nanoseconds['ark_bls12_381_g1_proj_double'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_double') + nanoseconds['ark_bls12_381_g1_proj_eq'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_eq') + nanoseconds['ark_bls12_381_g1_proj_generator'] = 1 + nanoseconds['ark_bls12_381_g1_proj_infinity'] = 1 + nanoseconds['ark_bls12_381_g1_proj_neg'] = 1 + nanoseconds['ark_bls12_381_g1_proj_scalar_mul'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_scalar_mul') + nanoseconds['ark_bls12_381_g1_proj_sub'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_sub') + nanoseconds['ark_bls12_381_g1_proj_to_affine'] = load_bench_ns.main('target/criterion/ark_bls12_381/g1_proj_to_affine') + nanoseconds['ark_bls12_381_g2_affine_deser_comp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_affine_deser_comp') + nanoseconds['ark_bls12_381_g2_affine_deser_uncomp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_affine_deser_uncomp') + nanoseconds['ark_bls12_381_g2_affine_serialize_comp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_affine_serialize_comp') + nanoseconds['ark_bls12_381_g2_affine_serialize_uncomp'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_affine_serialize_uncomp') + nanoseconds['ark_bls12_381_g2_proj_add'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_add') + nanoseconds['ark_bls12_381_g2_proj_double'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_double') + nanoseconds['ark_bls12_381_g2_proj_eq'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_eq') + nanoseconds['ark_bls12_381_g2_proj_generator'] = 1 + nanoseconds['ark_bls12_381_g2_proj_infinity'] = 1 + nanoseconds['ark_bls12_381_g2_proj_neg'] = 1 + nanoseconds['ark_bls12_381_g2_proj_scalar_mul'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_scalar_mul') + nanoseconds['ark_bls12_381_g2_proj_sub'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_sub') + nanoseconds['ark_bls12_381_g2_proj_to_affine'] = load_bench_ns.main('target/criterion/ark_bls12_381/g2_proj_to_affine') + nanoseconds['ark_bls12_381_pairing'] = load_bench_ns.main('target/criterion/ark_bls12_381/pairing') + _,_,nanoseconds['ark_bls12_381_multi_pairing_per_pair'],nanoseconds['ark_bls12_381_multi_pairing_base'] = get_bench_ns_linear('target/criterion/ark_bls12_381/pairing_product') + _,_,nanoseconds['ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte'],nanoseconds['ark_h2c_bls12381g1_xmd_sha256_sswu_base'] = get_bench_ns_linear('target/criterion/ark_bls12_381/hash_to_g1_proj') + _,_,nanoseconds['ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte'],nanoseconds['ark_h2c_bls12381g2_xmd_sha256_sswu_base'] = get_bench_ns_linear('target/criterion/ark_bls12_381/hash_to_g2_proj') + gas_units = {k:gas_per_ns*v for k,v in nanoseconds.items()} + lines = [f' [.algebra.{k}, {{ {TARGET_GAS_VERSION}.. => "algebra.{k}" }}, {prettify_number(v)} * MUL],' for k,v in sorted(gas_units.items())] + return lines + +def main(gas_per_ns): + path = Path('aptos-move/aptos-gas/src/aptos_framework.rs') + lines = path.read_text().split('\n') + line_id_begin = lines.index(' // Algebra gas parameters begin.') + line_id_end = lines.index(' // Algebra gas parameters end.') + generator_note_line = f' // Generated at time {time()} by `scripts/algebra-gas/update_algebra_gas_params.py` with gas_per_ns={gas_per_ns}.' + new_lines = lines[:line_id_begin+1] + [generator_note_line] + get_algebra_lines(gas_per_ns) + lines[line_id_end:] + path.write_text('\n'.join(new_lines)) + +if __name__=='__main__': + parser = argparse.ArgumentParser( + description='Generate gas parameters for algebra module in `aptos-move/aptos-gas/src/aptos_framework.rs`.') + parser.add_argument('--gas_per_ns', required=True, type=float) + args = parser.parse_args() + main(args.gas_per_ns) diff --git a/scripts/algebra-gas/utils.py b/scripts/algebra-gas/utils.py new file mode 100644 index 0000000000000..c222e8b1d39de --- /dev/null +++ b/scripts/algebra-gas/utils.py @@ -0,0 +1,9 @@ +import re + +def parse_range_from_dataset_path(dataset_path): + match = re.match(r'(.+)\.(\d+)-(\d+)\.json', dataset_path) + assert match!=None + base_path = match.group(1) + x_begin = int(match.group(2)) + x_end = int(match.group(3)) + return (base_path, x_begin, x_end)