diff --git a/Cargo.lock b/Cargo.lock index 785abc3f13..1500560b32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,16 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "altbn128" +version = "0.1.0" +dependencies = [ + "base16", + "casper-contract", + "casper-types", + "zeropool-bn", +] + [[package]] name = "anes" version = "0.1.6" @@ -443,9 +453,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -605,6 +615,7 @@ dependencies = [ "tracing", "uint", "walrus", + "zeropool-bn", ] [[package]] @@ -3494,6 +3505,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "leb128" @@ -3758,7 +3772,7 @@ dependencies = [ "log", "memchr", "mime", - "spin", + "spin 0.9.8", "version_check", ] @@ -5013,6 +5027,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -5425,6 +5445,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -6957,3 +6983,16 @@ name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" + +[[package]] +name = "zeropool-bn" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e61de68ede9ffdd69c01664f65a178c5188b73f78faa21f0936016a888ff7c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] diff --git a/execution_engine/Cargo.toml b/execution_engine/Cargo.toml index 343bf42e85..0fed2efaab 100644 --- a/execution_engine/Cargo.toml +++ b/execution_engine/Cargo.toml @@ -12,6 +12,7 @@ license = "Apache-2.0" [dependencies] anyhow = "1.0.33" +bn = { version = "0.5", package = "zeropool-bn", default-features = false } base16 = "0.2.1" bincode = "1.3.1" casper-storage = { version = "2.0.0", path = "../storage" } diff --git a/execution_engine/src/resolvers/v1_function_index.rs b/execution_engine/src/resolvers/v1_function_index.rs index 3ec135cd00..142f3a98b0 100644 --- a/execution_engine/src/resolvers/v1_function_index.rs +++ b/execution_engine/src/resolvers/v1_function_index.rs @@ -60,6 +60,9 @@ pub(crate) enum FunctionIndex { ManageMessageTopic, EmitMessage, LoadCallerInformation, + AltBn128Add, + AltBn128Mul, + AltBn128Pairing, } impl From for usize { diff --git a/execution_engine/src/resolvers/v1_resolver.rs b/execution_engine/src/resolvers/v1_resolver.rs index fa35f8f3d7..a211fc02aa 100644 --- a/execution_engine/src/resolvers/v1_resolver.rs +++ b/execution_engine/src/resolvers/v1_resolver.rs @@ -253,6 +253,18 @@ impl ModuleImportResolver for RuntimeModuleImportResolver { Signature::new(&[ValueType::I32; 4][..], Some(ValueType::I32)), FunctionIndex::EmitMessage.into(), ), + "casper_alt_bn128_add" => FuncInstance::alloc_host( + Signature::new(&[ValueType::I32; 6][..], Some(ValueType::I32)), + FunctionIndex::AltBn128Add.into(), + ), + "casper_alt_bn128_mul" => FuncInstance::alloc_host( + Signature::new(&[ValueType::I32; 5][..], Some(ValueType::I32)), + FunctionIndex::AltBn128Mul.into(), + ), + "casper_alt_bn128_pairing" => FuncInstance::alloc_host( + Signature::new(&[ValueType::I32; 3][..], Some(ValueType::I32)), + FunctionIndex::AltBn128Pairing.into(), + ), _ => { return Err(InterpreterError::Function(format!( "host module doesn't export function with name {}", diff --git a/execution_engine/src/runtime/builtins.rs b/execution_engine/src/runtime/builtins.rs new file mode 100644 index 0000000000..0ea6316a2f --- /dev/null +++ b/execution_engine/src/runtime/builtins.rs @@ -0,0 +1,4 @@ +//! Builtin functions for the execution engine. +//! +//! Functions here are lifted to the host and are implemented in the host's native code. +pub mod altbn128; diff --git a/execution_engine/src/runtime/builtins/altbn128.rs b/execution_engine/src/runtime/builtins/altbn128.rs new file mode 100644 index 0000000000..d8af2470ff --- /dev/null +++ b/execution_engine/src/runtime/builtins/altbn128.rs @@ -0,0 +1,387 @@ +//! Implementation of the alt_bn128 curve operations. +use bn::{AffineG1, FieldError, Fq, Fr, Group, G1}; +use casper_types::U256; +use thiserror::Error; + +/// Errors that can occur when working with alt_bn128 curve. +#[derive(Debug, Error, PartialEq, Eq, PartialOrd, Ord)] +pub enum Error { + /// Invalid length. + #[error("Invalid length")] + InvalidLength = 1, + /// Invalid point x coordinate. + #[error("Invalid point x coordinate")] + InvalidXCoordinate = 2, + /// Invalid point y coordinate. + #[error("Invalid point y coordinate")] + InvalidYCoordinate = 3, + /// Invalid point. + #[error("Invalid point")] + InvalidPoint = 4, + /// Invalid A. + #[error("Invalid A")] + InvalidA = 5, + /// Invalid B. + #[error("Invalid B")] + InvalidB = 6, + /// Invalid Ax. + #[error("Invalid Ax")] + InvalidAx = 7, + /// Invalid Ay. + #[error("Invalid Ay")] + InvalidAy = 8, + /// Invalid Bay. + #[error("Invalid Bay")] + InvalidBay = 9, + /// Invalid Bax. + #[error("Invalid Bax")] + InvalidBax = 10, + /// Invalid Bby. + #[error("Invalid Bby")] + InvalidBby = 11, + /// Invalid Bbx. + #[error("Invalid Bbx")] + InvalidBbx = 12, +} + +fn point_from_coords(x: U256, y: U256) -> Result { + let px = Fq::from_slice(&x.to_be_bytes()).map_err(|_| Error::InvalidXCoordinate)?; + let py = Fq::from_slice(&y.to_be_bytes()).map_err(|_| Error::InvalidYCoordinate)?; + + Ok(if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py) + .map_err(|_| Error::InvalidPoint)? + .into() + }) +} + +fn fq_to_u256(fq: Fq) -> U256 { + let mut buf = [0u8; 32]; + fq.to_big_endian(&mut buf).unwrap(); + U256::from_big_endian(&buf) +} + +fn fq_from_u256(u256: U256) -> Result { + let mut buf = [0u8; 32]; + u256.to_big_endian(&mut buf); + Fq::from_slice(&buf) +} + +/// Adds two points on the alt_bn128 curve. +pub fn alt_bn128_add(x1: U256, y1: U256, x2: U256, y2: U256) -> Result<(U256, U256), Error> { + let p1 = point_from_coords(x1, y1)?; + let p2 = point_from_coords(x2, y2)?; + + let mut x = U256::zero(); + let mut y = U256::zero(); + + if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { + x = fq_to_u256(sum.x()); + y = fq_to_u256(sum.y()); + } + + Ok((x, y)) +} + +/// Multiplies a point on the alt_bn128 curve by a scalar. +pub fn alt_bn128_mul(x: U256, y: U256, scalar: U256) -> Result<(U256, U256), Error> { + let p = point_from_coords(x, y)?; + + let mut x = U256::zero(); + let mut y = U256::zero(); + let fr = Fr::from_slice(&scalar.to_be_bytes()).map_err(|_| Error::InvalidPoint)?; + + if let Some(product) = AffineG1::from_jacobian(p * fr) { + x = fq_to_u256(product.x()); + y = fq_to_u256(product.y()); + } + + Ok((x, y)) +} + +/// Pairing check for a list of points. +pub fn alt_bn128_pairing(values: Vec<(U256, U256, U256, U256, U256, U256)>) -> Result { + let mut pairs = Vec::with_capacity(values.len()); + for (ax, ay, bax, bay, bbx, bby) in values { + let ax = fq_from_u256(ax).map_err(|_| Error::InvalidAx)?; + let ay = fq_from_u256(ay).map_err(|_| Error::InvalidAy)?; + let bax = fq_from_u256(bax).map_err(|_| Error::InvalidBax)?; + let bay = fq_from_u256(bay).map_err(|_| Error::InvalidBay)?; + let bbx = fq_from_u256(bbx).map_err(|_| Error::InvalidBbx)?; + let bby = fq_from_u256(bby).map_err(|_| Error::InvalidBby)?; + + let g1_a = { + if ax.is_zero() && ay.is_zero() { + bn::G1::zero() + } else { + bn::AffineG1::new(ax, ay) + .map_err(|_| Error::InvalidA)? + .into() + } + }; + let g1_b = { + let ba = bn::Fq2::new(bax, bay); + let bb = bn::Fq2::new(bbx, bby); + + if ba.is_zero() && bb.is_zero() { + bn::G2::zero() + } else { + bn::AffineG2::new(ba, bb) + .map_err(|_| Error::InvalidB)? + .into() + } + }; + + pairs.push((g1_a, g1_b)); + } + + Ok(bn::pairing_batch(pairs.as_slice()) == bn::Gt::one()) +} + +#[cfg(test)] +mod tests { + use casper_types::U256; + + use super::*; + + #[test] + fn test_alt_bn128_add() { + let x1 = U256::from_str_radix( + "18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9", + 16, + ) + .unwrap(); + + let y1 = U256::from_str_radix( + "063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", + 16, + ) + .unwrap(); + + let x2 = U256::from_str_radix( + "07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed", + 16, + ) + .unwrap(); + let y2 = U256::from_str_radix( + "06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + 16, + ) + .unwrap(); + + let expected_x = U256::from_str_radix( + "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703", + 16, + ) + .unwrap(); + let expected_y = U256::from_str_radix( + "301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + 16, + ) + .unwrap(); + + dbg!( + x1.to_le_bytes(), + y1.to_le_bytes(), + x2.to_le_bytes(), + y2.to_le_bytes(), + expected_x.to_le_bytes(), + expected_y.to_le_bytes(), + ); + + let result = alt_bn128_add(x1, y1, x2, y2); + assert_eq!(result, Ok((expected_x, expected_y))); + } + + #[test] + fn zero() { + assert_eq!( + alt_bn128_add(U256::zero(), U256::zero(), U256::zero(), U256::zero()), + Ok((U256::zero(), U256::zero())) + ); + } + + #[test] + fn add_error() { + let all_ones = U256::from_str_radix( + "1111111111111111111111111111111111111111111111111111111111111111", + 16, + ) + .unwrap(); + + assert_eq!( + alt_bn128_add(all_ones, all_ones, all_ones, all_ones), + Err(Error::InvalidPoint), + ); + } + + #[test] + fn test_alt_bn128_mul() { + let x = U256::from_str_radix( + "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7", + 16, + ) + .unwrap(); + let y = U256::from_str_radix( + "21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204", + 16, + ) + .unwrap(); + let scalar = U256::from_str_radix( + "00000000000000000000000000000000000000000000000011138ce750fa15c2", + 16, + ) + .unwrap(); + + let expected_x = U256::from_str_radix( + "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c", + 16, + ) + .unwrap(); + let expected_y = U256::from_str_radix( + "031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + 16, + ) + .unwrap(); + + dbg!( + x.to_le_bytes(), + y.to_le_bytes(), + scalar.to_le_bytes(), + expected_x.to_le_bytes(), + expected_y.to_le_bytes() + ); + + assert_eq!(alt_bn128_mul(x, y, scalar), Ok((expected_x, expected_y))); + } + + #[test] + fn test_zero_multiplication() { + // zero multiplication test + + let x = U256::from_str_radix( + "0000000000000000000000000000000000000000000000000000000000000000", + 16, + ) + .unwrap(); + let y = U256::from_str_radix( + "0000000000000000000000000000000000000000000000000000000000000000", + 16, + ) + .unwrap(); + let scalar = U256::from_str_radix( + "0200000000000000000000000000000000000000000000000000000000000000", + 16, + ) + .unwrap(); + + let expected_x = U256::from_str_radix( + "0000000000000000000000000000000000000000000000000000000000000000", + 16, + ) + .unwrap(); + + let expected_y = U256::from_str_radix( + "0000000000000000000000000000000000000000000000000000000000000000", + 16, + ) + .unwrap(); + + assert_eq!(alt_bn128_mul(x, y, scalar), Ok((expected_x, expected_y))); + } + + #[test] + fn test_not_on_curve_multiplication() { + // point not on curve fail + + let x = U256::from_str_radix( + "1111111111111111111111111111111111111111111111111111111111111111", + 16, + ) + .unwrap(); + let y = U256::from_str_radix( + "1111111111111111111111111111111111111111111111111111111111111111", + 16, + ) + .unwrap(); + let scalar = U256::from_str_radix( + "0f00000000000000000000000000000000000000000000000000000000000000", + 16, + ) + .unwrap(); + assert_eq!(alt_bn128_mul(x, y, scalar), Err(Error::InvalidPoint)); + } + + #[test] + fn test_pairing() { + let ax_1 = U256::from_str_radix( + "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59", + 16, + ) + .unwrap(); + let ay_1 = U256::from_str_radix( + "3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41", + 16, + ) + .unwrap(); + let bay_1 = U256::from_str_radix( + "209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7", + 16, + ) + .unwrap(); + let bax_1 = U256::from_str_radix( + "04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678", + 16, + ) + .unwrap(); + let bby_1 = U256::from_str_radix( + "2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d", + 16, + ) + .unwrap(); + let bbx_1 = U256::from_str_radix( + "120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550", + 16, + ) + .unwrap(); + + let ax_2 = U256::from_str_radix( + "111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c", + 16, + ) + .unwrap(); + let ay_2 = U256::from_str_radix( + "2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411", + 16, + ) + .unwrap(); + let bay_2 = U256::from_str_radix( + "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", + 16, + ) + .unwrap(); + let bax_2 = U256::from_str_radix( + "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", + 16, + ) + .unwrap(); + let bby_2 = U256::from_str_radix( + "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", + 16, + ) + .unwrap(); + let bbx_2 = U256::from_str_radix( + "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + 16, + ) + .unwrap(); + + let result = alt_bn128_pairing(vec![ + (ax_1, ay_1, bax_1, bay_1, bbx_1, bby_1), + (ax_2, ay_2, bax_2, bay_2, bbx_2, bby_2), + ]); + assert!(result.expect("Pairing failed")); + } +} diff --git a/execution_engine/src/runtime/externals.rs b/execution_engine/src/runtime/externals.rs index d8f76bc123..d8619e9a39 100644 --- a/execution_engine/src/runtime/externals.rs +++ b/execution_engine/src/runtime/externals.rs @@ -14,11 +14,11 @@ use casper_types::{ contract_messages::MessageTopicOperation, contracts::ContractPackageHash, crypto, AddressableEntityHash, ApiError, EntityVersion, Gas, Group, HostFunction, - HostFunctionCost, Key, PackageHash, PackageStatus, StoredValue, URef, U512, + HostFunctionCost, Key, PackageHash, PackageStatus, StoredValue, URef, U256, U512, UREF_SERIALIZED_LENGTH, }; -use super::{args::Args, ExecError, Runtime}; +use super::{args::Args, builtins, ExecError, Runtime}; use crate::resolvers::v1_function_index::FunctionIndex; impl<'a, R> Externals for Runtime<'a, R> @@ -1276,6 +1276,162 @@ where } Ok(Some(RuntimeValue::I32(api_error::i32_from(result)))) } + FunctionIndex::AltBn128Add => { + // args(0) = pointer to x1 in wasm memory + // args(1) = pointer to y1 in wasm memory + // args(2) = pointer to x2 in wasm memory + // args(3) = pointer to y2 in wasm memory + // args(4) = pointer to result_x in wasm memory + // args(5) = pointer to result_y in wasm memory + let (x1_ptr, y1_ptr, x2_ptr, y2_ptr, result_x_ptr, result_y_ptr): ( + u32, + u32, + u32, + u32, + u32, + u32, + ) = Args::parse(args)?; + self.charge_host_function_call( + &host_function_costs.alt_bn128_add, + [x1_ptr, y1_ptr, x2_ptr, y2_ptr, result_x_ptr, result_y_ptr], + )?; + + let x1: Vec = self.bytes_from_mem(x1_ptr, 32)?; + let y1: Vec = self.bytes_from_mem(y1_ptr, 32)?; + let x2: Vec = self.bytes_from_mem(x2_ptr, 32)?; + let y2: Vec = self.bytes_from_mem(y2_ptr, 32)?; + + let x1: U256 = U256::from_little_endian(&x1); + let y1: U256 = U256::from_little_endian(&y1); + let x2: U256 = U256::from_little_endian(&x2); + let y2: U256 = U256::from_little_endian(&y2); + + match builtins::altbn128::alt_bn128_add(x1, y1, x2, y2) { + Ok((x, y)) => { + let x_bytes = x.to_le_bytes(); + let y_bytes = y.to_le_bytes(); + + self.try_get_memory()? + .set(result_x_ptr, &x_bytes) + .map_err(|error| ExecError::Interpreter(error.into()))?; + + self.try_get_memory()? + .set(result_y_ptr, &y_bytes) + .map_err(|error| ExecError::Interpreter(error.into()))?; + + // Success + Ok(Some(RuntimeValue::I32(0))) + } + Err(error) => { + let value = error as i32; + debug_assert_ne!(value, 0); + Ok(Some(RuntimeValue::I32(value))) + } + } + } + FunctionIndex::AltBn128Mul => { + // args(0) = pointer to x in wasm memory + // args(1) = pointer to y in wasm memory + // args(2) = pointer to scalar in wasm memory + // args(3) = pointer to result_x in wasm memory + // args(4) = pointer to result_y in wasm memory + let (x_ptr, y_ptr, scalar_ptr, result_x_ptr, result_y_ptr): ( + u32, + u32, + u32, + u32, + u32, + ) = Args::parse(args)?; + self.charge_host_function_call( + &host_function_costs.alt_bn128_mul, + [x_ptr, y_ptr, scalar_ptr, result_x_ptr, result_y_ptr], + )?; + + let x: Vec = self.bytes_from_mem(x_ptr, 32)?; + let y: Vec = self.bytes_from_mem(y_ptr, 32)?; + let scalar: Vec = self.bytes_from_mem(scalar_ptr, 32)?; + + let x: U256 = U256::from_little_endian(&x); + let y: U256 = U256::from_little_endian(&y); + let scalar: U256 = U256::from_little_endian(&scalar); + match builtins::altbn128::alt_bn128_mul(x, y, scalar) { + Ok((x, y)) => { + let x_bytes = x.to_le_bytes(); + let y_bytes = y.to_le_bytes(); + + self.try_get_memory()? + .set(result_x_ptr, &x_bytes) + .map_err(|error| ExecError::Interpreter(error.into()))?; + + self.try_get_memory()? + .set(result_y_ptr, &y_bytes) + .map_err(|error| ExecError::Interpreter(error.into()))?; + + // Success + Ok(Some(RuntimeValue::I32(0))) + } + Err(error) => { + let value = error as i32; + debug_assert_ne!(value, 0); + Ok(Some(RuntimeValue::I32(value))) + } + } + } + FunctionIndex::AltBn128Pairing => { + // args(0) = pointer to elements in wasm memory + // args(1) = size of elements in wasm memory + // args(2) = pointer to result in wasm memory + let (elements_ptr, elements_size, result_ptr): (u32, u32, u32) = Args::parse(args)?; + self.charge_host_function_call( + &host_function_costs.alt_bn128_pairing, + [elements_ptr, elements_size, result_ptr], + )?; + + const PAIR_ELEMENT_LEN: usize = 192; + + if (elements_size as usize) % PAIR_ELEMENT_LEN != 0 { + return Ok(Some(RuntimeValue::I32( + builtins::altbn128::Error::InvalidLength as _, + ))); + } + let inputs = + self.checked_memory_slice(elements_ptr as _, elements_size as _, |chunk| { + chunk + .chunks_exact(PAIR_ELEMENT_LEN) + .map(|pair| { + debug_assert_eq!(pair.len(), 192); + let (ax, ay, bay, bax, bby, bbx) = ( + // These values are coming from a byte representation of an + // array, so we can't expect the memory layout of individual + // [u8;32] individual values are in little endian form. + U256::from_little_endian(&pair[0..32]), + U256::from_little_endian(&pair[32..64]), + U256::from_little_endian(&pair[64..96]), + U256::from_little_endian(&pair[96..128]), + U256::from_little_endian(&pair[128..160]), + U256::from_little_endian(&pair[160..192]), + ); + (ax, ay, bay, bax, bby, bbx) + }) + .collect::>() + })?; + + match builtins::altbn128::alt_bn128_pairing(inputs) { + Ok(result) => { + let result_value = result as u32; + let result_bytes = result_value.to_le_bytes(); + self.try_get_memory()? + .set(result_ptr, &result_bytes) + .map_err(|error| ExecError::Interpreter(error.into()))?; + Ok(Some(RuntimeValue::I32(0))) + } + Err(error) => { + let value = error as i32; + debug_assert_ne!(value, 0); + Ok(Some(RuntimeValue::I32(value))) + } + } + } } } } diff --git a/execution_engine/src/runtime/mod.rs b/execution_engine/src/runtime/mod.rs index 8debe05174..b51f6cf88c 100644 --- a/execution_engine/src/runtime/mod.rs +++ b/execution_engine/src/runtime/mod.rs @@ -1,6 +1,7 @@ //! This module contains executor state of the WASM code. mod args; mod auction_internal; +pub mod builtins; mod externals; mod handle_payment_internal; mod host_function_flag; diff --git a/execution_engine_testing/tests/src/test/contract_api/builtins.rs b/execution_engine_testing/tests/src/test/contract_api/builtins.rs new file mode 100644 index 0000000000..93ca24a535 --- /dev/null +++ b/execution_engine_testing/tests/src/test/contract_api/builtins.rs @@ -0,0 +1,40 @@ +use casper_types::{runtime_args, system::standard_payment::ARG_AMOUNT}; +use rand::Rng; + +use casper_engine_test_support::{ + DeployItemBuilder, ExecuteRequestBuilder, LmdbWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, + DEFAULT_PAYMENT, LOCAL_GENESIS_REQUEST, +}; + +const ALTBN128_WASM: &str = "altbn128.wasm"; + +#[ignore] +#[test] +fn altbn128_builtins_should_work() { + let mut builder = LmdbWasmTestBuilder::default(); + builder.run_genesis(LOCAL_GENESIS_REQUEST.clone()); + + let mut rng = rand::thread_rng(); + let deploy_hash = rng.gen(); + let address = *DEFAULT_ACCOUNT_ADDR; + let deploy_item = DeployItemBuilder::new() + .with_address(address) + .with_session_code(ALTBN128_WASM, runtime_args! {}) + .with_standard_payment(runtime_args! { + ARG_AMOUNT => *DEFAULT_PAYMENT + }) + .with_authorization_keys(&[address]) + .with_deploy_hash(deploy_hash) + .build(); + let execute_request = ExecuteRequestBuilder::from_deploy_item(&deploy_item).build(); + + builder.exec(execute_request).commit().expect_success(); + + // Compile above test contract with either feature flags: wasm_add_test, wasm_mul_test, + // wasm_pairing_test, and uncomment gas line. None of these flags will use host functions. + // bash -c 'cd smart_contracts/contracts && + // RUSTFLAGS="--remap-path-prefix=$HOME/.cargo= --remap-path-prefix=$PWD=/dir" cargo --locked + // build --verbose --release --package altbn128 --features wasm_add_test' + + // eprintln!("gas {}", builder.last_exec_gas_cost()); +} diff --git a/execution_engine_testing/tests/src/test/contract_api/mod.rs b/execution_engine_testing/tests/src/test/contract_api/mod.rs index 4b9b90f553..d3422c4be2 100644 --- a/execution_engine_testing/tests/src/test/contract_api/mod.rs +++ b/execution_engine_testing/tests/src/test/contract_api/mod.rs @@ -1,5 +1,6 @@ mod account; mod add_contract_version; +mod builtins; mod create_purse; mod dictionary; mod get_arg; diff --git a/node/src/utils/chain_specification.rs b/node/src/utils/chain_specification.rs index 26e860192f..1b9b86d741 100644 --- a/node/src/utils/chain_specification.rs +++ b/node/src/utils/chain_specification.rs @@ -250,6 +250,9 @@ mod tests { manage_message_topic: HostFunction::new(100, [0, 1, 2, 4]), emit_message: HostFunction::new(100, [0, 1, 2, 3]), cost_increase_per_message: 50, + alt_bn128_add: HostFunction::new(100, [0, 1, 2, 3, 4, 5]), + alt_bn128_mul: HostFunction::new(100, [0, 1, 2, 3, 4]), + alt_bn128_pairing: HostFunction::new(100, [0, 1, 2]), }); static EXPECTED_GENESIS_WASM_COSTS: Lazy = Lazy::new(|| { WasmConfig::new( diff --git a/resources/local/chainspec.toml.in b/resources/local/chainspec.toml.in index 69c5b92b2a..6bda9c3aa2 100644 --- a/resources/local/chainspec.toml.in +++ b/resources/local/chainspec.toml.in @@ -318,6 +318,9 @@ enable_contract_version = { cost = 200, arguments = [0, 0, 0, 0] } manage_message_topic = { cost = 200, arguments = [0, 30_000, 0, 0] } emit_message = { cost = 200, arguments = [0, 30_000, 0, 120_000] } cost_increase_per_message = 50 +alt_bn128_add = { cost = 1_000_000, arguments = [0, 0, 0, 0, 0, 0] } +alt_bn128_mul = { cost = 1_000_000, arguments = [0, 0, 0, 0, 0] } +alt_bn128_pairing = { cost = 1_000_000, arguments = [0, 2_000, 0] } [wasm.messages_limits] max_topic_name_size = 256 diff --git a/resources/production/chainspec.toml b/resources/production/chainspec.toml index ad9bdcabf2..fef25fbb5b 100644 --- a/resources/production/chainspec.toml +++ b/resources/production/chainspec.toml @@ -328,6 +328,9 @@ enable_contract_version = { cost = 200, arguments = [0, 0, 0, 0] } manage_message_topic = { cost = 200, arguments = [0, 30_000, 0, 0] } emit_message = { cost = 200, arguments = [0, 30_000, 0, 120_000] } cost_increase_per_message = 50 +alt_bn128_add = { cost = 1_000_000, arguments = [0, 0, 0, 0, 0, 0] } +alt_bn128_mul = { cost = 1_000_000, arguments = [0, 0, 0, 0, 0] } +alt_bn128_pairing = { cost = 1_000_000, arguments = [0, 2_000, 0] } [wasm.messages_limits] max_topic_name_size = 256 diff --git a/smart_contracts/contract/src/contract_api/builtins.rs b/smart_contracts/contract/src/contract_api/builtins.rs new file mode 100644 index 0000000000..5dcce31d20 --- /dev/null +++ b/smart_contracts/contract/src/contract_api/builtins.rs @@ -0,0 +1,4 @@ +//! Built-in functions provided by the Casper runtime. +//! +//! These functions are lifting otherwise very expensive operations to the host. +pub mod altbn128; diff --git a/smart_contracts/contract/src/contract_api/builtins/altbn128.rs b/smart_contracts/contract/src/contract_api/builtins/altbn128.rs new file mode 100644 index 0000000000..f226bc32fd --- /dev/null +++ b/smart_contracts/contract/src/contract_api/builtins/altbn128.rs @@ -0,0 +1,250 @@ +//! Host-optimized support for pairing cryptography with the Barreto-Naehrig curve +use core::{array::TryFromSliceError, ffi::c_void, mem::MaybeUninit}; + +use casper_types::U256; + +use crate::ext_ffi; + +/// A point on the alt_bn128 curve. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[repr(C, packed)] +pub struct G1([u8; 32]); + +impl G1 { + /// Returns a point with all bytes set to zero. + pub const fn zero() -> Self { + G1([0; 32]) + } + + /// Returns the inner byte array. + pub fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } +} + +impl From<[u8; 32]> for G1 { + fn from(value: [u8; 32]) -> Self { + G1(value) + } +} + +impl From for G1 { + fn from(value: U256) -> Self { + let bytes = value.to_le_bytes(); + G1(bytes) + } +} + +/// A scalar on the alt_bn128 curve. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[repr(C, packed)] +pub struct Fr([u8; 32]); + +impl Fr { + /// Returns a point with all bytes set to zero. + pub const fn zero() -> Self { + Fr([0; 32]) + } + + /// Returns the inner byte array. + pub fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } +} + +impl From for Fr { + fn from(value: U256) -> Self { + let bytes = value.to_le_bytes(); + Self(bytes) + } +} + +impl From<[u8; 32]> for Fr { + fn from(value: [u8; 32]) -> Self { + Fr(value) + } +} + +/// A field element on the alt_bn128 curve. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[repr(C, packed)] +pub struct Fq([u8; 32]); + +impl Fq { + /// Returns a point with all bytes set to zero. + pub const fn zero() -> Self { + Fq([0; 32]) + } + + /// Returns the inner byte array. + pub fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } +} + +impl From for Fq { + fn from(value: U256) -> Self { + let bytes = value.to_le_bytes(); + Self(bytes) + } +} + +impl TryFrom<&[u8]> for Fq { + type Error = TryFromSliceError; + + fn try_from(value: &[u8]) -> core::result::Result { + Ok(Fq(value.try_into()?)) + } +} + +impl From<[u8; 32]> for Fq { + fn from(value: [u8; 32]) -> Self { + Fq(value) + } +} + +/// Error type for the alt_bn128 module. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[repr(i32)] +pub enum Error { + /// Invalid length. + InvalidLength = 1, + /// Invalid point x coordinate. + InvalidXCoordinate = 2, + /// Invalid point y coordinate. + InvalidYCoordinate = 3, + /// Invalid point. + InvalidPoint = 4, + /// Invalid A. + InvalidA = 5, + /// Invalid B. + InvalidB = 6, + /// Invalid Ax. + InvalidAx = 7, + /// Invalid Ay. + InvalidAy = 8, + /// Invalid Bay. + InvalidBay = 9, + /// Invalid Bax. + InvalidBax = 10, + /// Invalid Bby. + InvalidBby = 11, + /// Invalid Bbx. + InvalidBbx = 12, + /// Unknown error. + Unknown(i32), +} + +impl From for Error { + fn from(value: i32) -> Self { + match value { + 1 => Error::InvalidLength, + 2 => Error::InvalidXCoordinate, + 3 => Error::InvalidYCoordinate, + 4 => Error::InvalidPoint, + 5 => Error::InvalidA, + 6 => Error::InvalidB, + 7 => Error::InvalidAx, + 8 => Error::InvalidAy, + 9 => Error::InvalidBay, + 10 => Error::InvalidBax, + 11 => Error::InvalidBby, + 12 => Error::InvalidBbx, + value => Error::Unknown(value), + } + } +} + +/// Result type for the alt_bn128 module. +pub type Result = core::result::Result; + +/// Adds two points on the alt_bn128 curve. +pub fn alt_bn128_add(x1: &G1, y1: &G1, x2: &G1, y2: &G1) -> Result<(Fq, Fq)> { + let mut x_buf = [0; 32]; + let mut y_buf = [0; 32]; + + let ret = unsafe { + ext_ffi::casper_alt_bn128_add( + x1.0.as_ptr(), + y1.0.as_ptr(), + x2.0.as_ptr(), + y2.0.as_ptr(), + x_buf.as_mut_ptr(), + y_buf.as_mut_ptr(), + ) + }; + + if ret == 0 { + Ok((Fq(x_buf), Fq(y_buf))) + } else { + Err(Error::from(ret)) + } +} + +/// Multiplies a point on the alt_bn128 curve by a scalar. +pub fn alt_bn128_mul(x: &G1, y: &G1, scalar: &Fr) -> Result<(Fq, Fq)> { + let mut x_buf = [0; 32]; + let mut y_buf = [0; 32]; + + let ret = unsafe { + ext_ffi::casper_alt_bn128_mul( + x.0.as_ptr(), + y.0.as_ptr(), + scalar.0.as_ptr(), + x_buf.as_mut_ptr(), + y_buf.as_mut_ptr(), + ) + }; + + if ret == 0 { + Ok((Fq(x_buf), Fq(y_buf))) + } else { + Err(Error::from(ret)) + } +} + +/// A pairing of points on the alt_bn128 curve. +#[derive(Copy, Clone)] +#[repr(C, packed)] +pub struct Pair { + /// G1 point + pub ax: Fq, + /// G1 point + pub ay: Fq, + /// G2 point + pub bax: Fq, + /// G2 point + pub bay: Fq, + /// G1 point + pub bbx: Fq, + /// G1 point + pub bby: Fq, +} + +const _: () = assert!( + core::mem::size_of::() == 192, + "Pair size is not correct", +); + +/// Performs a pairing of points on the alt_bn128 curve. +pub fn alt_bn128_pairing(points: &[Pair]) -> Result { + let mut result = MaybeUninit::uninit(); + + let bytes: &[u8] = unsafe { + core::slice::from_raw_parts(points.as_ptr() as *const u8, core::mem::size_of_val(points)) + }; + + let ret = unsafe { + ext_ffi::casper_alt_bn128_pairing( + bytes.as_ptr() as *const c_void, + bytes.len(), + result.as_mut_ptr(), + ) + }; + + if ret == 0 { + Ok(unsafe { result.assume_init() } != 0) + } else { + Err(Error::from(ret)) + } +} diff --git a/smart_contracts/contract/src/contract_api/mod.rs b/smart_contracts/contract/src/contract_api/mod.rs index 524a8f0705..c6979b605b 100644 --- a/smart_contracts/contract/src/contract_api/mod.rs +++ b/smart_contracts/contract/src/contract_api/mod.rs @@ -1,6 +1,7 @@ //! Contains support for writing smart contracts. pub mod account; +pub mod builtins; pub mod runtime; pub mod storage; pub mod system; diff --git a/smart_contracts/contract/src/ext_ffi.rs b/smart_contracts/contract/src/ext_ffi.rs index a8d5ec6df1..fb997cea3a 100644 --- a/smart_contracts/contract/src/ext_ffi.rs +++ b/smart_contracts/contract/src/ext_ffi.rs @@ -3,6 +3,8 @@ //! Generally should not be used directly. See the [`contract_api`](crate::contract_api) for //! high-level bindings suitable for writing smart contracts. +use core::ffi::c_void; + #[cfg(doc)] use alloc::collections::BTreeMap; @@ -843,4 +845,100 @@ extern "C" { call_stack_len_ptr: *mut usize, result_size_ptr: *mut usize, ) -> i32; + + /// Adds two points on the alt_bn128 elliptic curve. + /// + /// This function performs the addition of two points on the alt_bn128 elliptic curve and stores + /// the result in the provided pointers. + /// + /// # Parameters + /// + /// - `x1_ptr`: A pointer to the x-coordinate of the first point. + /// - `y1_ptr`: A pointer to the y-coordinate of the first point. + /// - `x2_ptr`: A pointer to the x-coordinate of the second point. + /// - `y2_ptr`: A pointer to the y-coordinate of the second point. + /// - `result_x_ptr`: A mutable pointer to store the x-coordinate of the resulting point. + /// - `result_y_ptr`: A mutable pointer to store the y-coordinate of the resulting point. + /// + /// # Returns + /// + /// - `0` if the addition was successful. + /// - `1` if the X is an invalid coordinate. + /// - `2` if the Y is an invalid coordinate. + /// - `3` if the point is not on a curve. + /// + /// # Safety + /// + /// This function is unsafe because it dereferences raw pointers. Ensure that the pointers are + /// valid and properly aligned before calling this function. + /// ``` + pub fn casper_alt_bn128_add( + x1_ptr: *const u8, + y1_ptr: *const u8, + x2_ptr: *const u8, + y2_ptr: *const u8, + result_x_ptr: *mut u8, + result_y_ptr: *mut u8, + ) -> i32; + + /// Multiplies a point on the alt_bn128 elliptic curve by a scalar. + /// + /// This function performs scalar multiplication of a point on the alt_bn128 elliptic curve and + /// stores the result in the provided pointers. + /// + /// # Parameters + /// + /// - `x_ptr`: A pointer to the x-coordinate of the point. + /// - `y_ptr`: A pointer to the y-coordinate of the point. + /// - `scalar_ptr`: A pointer to the scalar value. + /// - `result_x_ptr`: A mutable pointer to store the x-coordinate of the resulting point. + /// - `result_y_ptr`: A mutable pointer to store the y-coordinate of the resulting point. + /// + /// # Returns + /// + /// - `0` if the multiplication was successful. + /// - A non-zero integer if there was an error. + /// + /// # Safety + /// + /// This function is unsafe because it dereferences raw pointers. Ensure that the pointers are + /// valid and properly aligned before calling this function. + /// ``` + pub fn casper_alt_bn128_mul( + x_ptr: *const u8, + y_ptr: *const u8, + scalar_ptr: *const u8, + result_x_ptr: *mut u8, + result_y_ptr: *mut u8, + ) -> i32; + + /// Performs a pairing check on the alt_bn128 elliptic curve. + /// + /// This function performs a pairing check on the alt_bn128 elliptic curve using the provided + /// elements and stores the result in the provided pointer. + /// + /// # Parameters + /// + /// - `elements_ptr`: A pointer to the elements to be checked. This should be pointed at byte + /// array of multiple of 6 elements representing (ax, ay, bax, bay, bbx, bby). Each point is + /// 32 bytes long. + /// - `elements_size`: The size of the elements in bytes. + /// - `result_ptr`: A mutable pointer to store the result of the pairing check. The result will + /// be `1` if the pairing check is successful, and `0` otherwise. + /// + /// # Returns + /// + /// - `0` if the pairing check was successful. + /// - A non-zero integer if there was an error. + /// + /// # Safety + /// + /// This function is unsafe because it dereferences raw pointers. Ensure that the pointers are + /// valid and properly aligned before calling this function. + /// ``` + pub fn casper_alt_bn128_pairing( + elements_ptr: *const c_void, + elements_size: usize, + result_ptr: *mut i32, + ) -> i32; } diff --git a/smart_contracts/contracts/test/altbn128/Cargo.toml b/smart_contracts/contracts/test/altbn128/Cargo.toml new file mode 100644 index 0000000000..f067a5526d --- /dev/null +++ b/smart_contracts/contracts/test/altbn128/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "altbn128" +version = "0.1.0" +authors = ["MichaƂ Papierski "] +edition = "2021" + +[[bin]] +name = "altbn128" +path = "src/main.rs" +bench = false +doctest = false +test = false + +[dependencies] +casper-contract = { path = "../../../contract", features = ["test-support"] } +casper-types = { path = "../../../../types" } +base16 = { version = "0.2.1", default-features = false, features = [] } +bn = { version = "0.5", package = "zeropool-bn", default-features = false, optional = true } + +[features] +default = [] +wasm_add_test = ["bn"] +wasm_mul_test = ["bn"] +wasm_pairing_test = ["bn"] diff --git a/smart_contracts/contracts/test/altbn128/src/host.rs b/smart_contracts/contracts/test/altbn128/src/host.rs new file mode 100644 index 0000000000..2878308848 --- /dev/null +++ b/smart_contracts/contracts/test/altbn128/src/host.rs @@ -0,0 +1,279 @@ +use alloc::format; +use core::{ffi::c_void, mem::MaybeUninit}; + +use casper_contract::{ + contract_api::{ + builtins::altbn128::{self, Error, Fq, Fr, Pair, G1}, + runtime, + }, + ext_ffi, +}; +use casper_types::ApiError; + +const ADD_X1_LE: [u8; 32] = [ + 201, 61, 9, 78, 3, 116, 59, 93, 12, 97, 145, 70, 18, 221, 17, 179, 133, 113, 142, 54, 17, 84, + 219, 118, 2, 195, 194, 180, 207, 138, 177, 24, +]; +const ADD_Y1_LE: [u8; 32] = [ + 102, 114, 243, 152, 129, 40, 13, 252, 46, 211, 88, 150, 129, 150, 87, 117, 73, 167, 159, 245, + 185, 76, 19, 181, 12, 132, 32, 71, 156, 144, 60, 6, +]; +const ADD_X2_LE: [u8; 32] = [ + 237, 78, 1, 106, 124, 158, 1, 136, 58, 150, 146, 44, 255, 32, 127, 24, 26, 187, 192, 43, 156, + 12, 240, 69, 97, 189, 132, 138, 245, 183, 194, 7, +]; +const ADD_Y2_LE: [u8; 32] = [ + 215, 23, 250, 120, 132, 120, 214, 43, 116, 92, 72, 164, 6, 23, 54, 223, 23, 154, 76, 247, 163, + 13, 215, 242, 64, 233, 71, 193, 32, 78, 97, 6, +]; +const ADD_EXPECTED_X_LE: [u8; 32] = [ + 3, 151, 131, 2, 226, 174, 238, 161, 95, 182, 230, 76, 10, 131, 94, 216, 77, 254, 163, 12, 172, + 69, 60, 61, 156, 75, 253, 94, 92, 82, 67, 34, +]; +const ADD_EXPECTED_Y_LE: [u8; 32] = [ + 21, 201, 149, 241, 72, 125, 94, 174, 185, 125, 83, 50, 117, 237, 14, 24, 35, 71, 150, 53, 204, + 33, 223, 9, 229, 168, 109, 190, 51, 29, 29, 48, +]; + +const MUL_X_LE: [u8; 32] = [ + 183, 159, 10, 26, 139, 38, 2, 29, 230, 72, 86, 174, 215, 3, 71, 76, 213, 185, 229, 156, 180, + 167, 92, 79, 146, 66, 177, 243, 208, 230, 211, 43, +]; +const MUL_Y_LE: [u8; 32] = [ + 4, 178, 253, 206, 102, 174, 12, 57, 200, 25, 70, 74, 173, 223, 73, 46, 206, 9, 9, 48, 112, 29, + 47, 94, 145, 133, 175, 166, 224, 28, 97, 33, +]; +const MUL_SCALAR_LE: [u8; 32] = [ + 194, 21, 250, 80, 231, 140, 19, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +]; +const MUL_EXPECTED_X_LE: [u8; 32] = [ + 92, 30, 195, 19, 156, 108, 42, 238, 164, 245, 83, 160, 116, 178, 71, 138, 239, 250, 232, 52, + 212, 41, 190, 228, 202, 83, 33, 152, 106, 141, 10, 7, +]; +const MUL_EXPECTED_Y_LE: [u8; 32] = [ + 252, 26, 152, 198, 206, 195, 14, 105, 21, 243, 240, 244, 75, 7, 67, 25, 240, 176, 213, 205, + 249, 137, 185, 255, 169, 163, 235, 20, 233, 140, 27, 3, +]; + +const PAIRING_AX_1_LE: [u8; 32] = [ + 89, 63, 196, 36, 96, 193, 49, 221, 100, 166, 173, 118, 170, 167, 255, 129, 51, 25, 161, 187, + 126, 213, 65, 69, 185, 75, 239, 77, 111, 71, 118, 28, +]; +const PAIRING_AY_1_LE: [u8; 32] = [ + 65, 239, 106, 167, 3, 155, 92, 228, 148, 210, 233, 211, 85, 155, 129, 252, 69, 135, 103, 28, + 129, 226, 254, 4, 226, 115, 246, 32, 41, 221, 52, 48, +]; +const PAIRING_BAX_1_LE: [u8; 32] = [ + 247, 91, 163, 3, 32, 69, 74, 107, 57, 20, 53, 198, 54, 150, 50, 167, 153, 207, 147, 26, 229, + 136, 216, 75, 108, 212, 245, 191, 94, 209, 157, 32, +]; +const PAIRING_BAY_1_LE: [u8; 32] = [ + 120, 22, 164, 21, 99, 75, 175, 73, 64, 192, 138, 76, 17, 96, 89, 144, 40, 141, 132, 97, 53, + 180, 52, 139, 250, 59, 72, 1, 202, 17, 191, 4, +]; +const PAIRING_BBX_1_LE: [u8; 32] = [ + 77, 52, 190, 81, 42, 60, 147, 191, 173, 31, 196, 122, 205, 26, 167, 162, 12, 253, 92, 68, 26, + 173, 162, 55, 53, 201, 207, 246, 74, 50, 184, 43, +]; +const PAIRING_BBY_1_LE: [u8; 32] = [ + 80, 117, 135, 222, 228, 229, 95, 22, 72, 176, 155, 12, 31, 230, 204, 162, 126, 224, 57, 254, + 198, 32, 95, 132, 249, 27, 12, 243, 76, 42, 10, 18, +]; +const PAIRING_AX_2_LE: [u8; 32] = [ + 124, 223, 182, 209, 73, 222, 34, 195, 234, 203, 241, 111, 60, 2, 162, 91, 250, 205, 15, 199, + 74, 28, 212, 16, 119, 9, 241, 28, 159, 18, 30, 17, +]; +const PAIRING_AY_2_LE: [u8; 32] = [ + 17, 244, 107, 106, 206, 250, 83, 56, 167, 112, 56, 185, 133, 53, 136, 162, 252, 66, 242, 43, + 70, 233, 109, 40, 23, 60, 14, 131, 26, 198, 50, 32, +]; +const PAIRING_BAX_2_LE: [u8; 32] = [ + 194, 18, 243, 174, 183, 133, 228, 151, 18, 231, 169, 53, 51, 73, 170, 241, 37, 93, 251, 49, + 183, 191, 96, 114, 58, 72, 13, 146, 147, 147, 142, 25, +]; +const PAIRING_BAY_2_LE: [u8; 32] = [ + 237, 246, 146, 217, 92, 189, 222, 70, 221, 218, 94, 247, 212, 34, 67, 103, 121, 68, 92, 94, + 102, 0, 106, 66, 118, 30, 31, 18, 239, 222, 0, 24, +]; +const PAIRING_BBX_2_LE: [u8; 32] = [ + 91, 151, 34, 209, 220, 218, 172, 85, 243, 142, 179, 112, 51, 49, 75, 188, 149, 51, 12, 105, + 173, 153, 158, 236, 117, 240, 95, 88, 208, 137, 6, 9, +]; +const PAIRING_BBY_2_LE: [u8; 32] = [ + 170, 125, 250, 102, 1, 204, 230, 76, 123, 211, 67, 12, 105, 231, 209, 227, 143, 64, 203, 141, + 128, 113, 171, 74, 235, 109, 140, 219, 165, 94, 200, 18, +]; +const ALL_ONES: [u8; 32] = [0x11; 32]; + +fn alt_bn128_pairing_raw(input: &[u8]) -> altbn128::Result { + let mut result = MaybeUninit::uninit(); + + let ret = unsafe { + ext_ffi::casper_alt_bn128_pairing( + input.as_ptr() as *const c_void, + input.len(), + result.as_mut_ptr(), + ) + }; + + if ret == 0 { + Ok(unsafe { result.assume_init() } != 0) + } else { + Err(Error::from(ret)) + } +} + +fn test_alt_bn128_add() { + let actual = altbn128::alt_bn128_add( + &G1::from(ADD_X1_LE), + &G1::from(ADD_Y1_LE), + &G1::from(ADD_X2_LE), + &G1::from(ADD_Y2_LE), + ); + let expected = Ok((Fq::from(ADD_EXPECTED_X_LE), Fq::from(ADD_EXPECTED_Y_LE))); + if actual != expected { + runtime::print(&format!("left {:?} right {:?}", actual, expected)); + runtime::revert(ApiError::User(line!() as _)); + } +} + +fn test_zero_add() { + if altbn128::alt_bn128_add(&G1::zero(), &G1::zero(), &G1::zero(), &G1::zero()) + != Ok((Fq::zero(), Fq::zero())) + { + runtime::revert(ApiError::User(line!() as _)); + } +} + +fn test_add_error() { + let all_ones = G1::from(ALL_ONES); + + if altbn128::alt_bn128_add(&all_ones, &all_ones, &all_ones, &all_ones) + != Err(Error::InvalidPoint) + { + runtime::revert(ApiError::User(line!() as _)); + } +} + +fn test_alt_bn128_mul() { + let x = G1::from(MUL_X_LE); + let y = G1::from(MUL_Y_LE); + let scalar = Fr::from(MUL_SCALAR_LE); + + assert_eq!( + altbn128::alt_bn128_mul(&x, &y, &scalar), + Ok((Fq::from(MUL_EXPECTED_X_LE), Fq::from(MUL_EXPECTED_Y_LE))) + ); +} +const BIG_SCALAR_0X02: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x02, +]; +const BIG_SCALAR_0X0F: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x0f, +]; + +fn test_zero_multiplication() { + let x = G1::zero(); + let y = G1::zero(); + let scalar = Fr::from(BIG_SCALAR_0X02); + assert_eq!( + altbn128::alt_bn128_mul(&x, &y, &scalar), + Ok((Fq::zero(), Fq::zero())) + ); +} + +fn test_not_on_curve_multiplication() { + let x = G1::from(ALL_ONES); + let y = G1::from(ALL_ONES); + let scalar = Fr::from(BIG_SCALAR_0X0F); + assert_eq!( + altbn128::alt_bn128_mul(&x, &y, &scalar), + Err(Error::InvalidPoint) + ); +} + +fn test_alt_bn128_pairing() { + let all_pairs = [ + Pair { + ax: Fq::from(PAIRING_AX_1_LE), + ay: Fq::from(PAIRING_AY_1_LE), + bax: Fq::from(PAIRING_BAX_1_LE), + bay: Fq::from(PAIRING_BAY_1_LE), + bbx: Fq::from(PAIRING_BBX_1_LE), + bby: Fq::from(PAIRING_BBY_1_LE), + }, + Pair { + ax: Fq::from(PAIRING_AX_2_LE), + ay: Fq::from(PAIRING_AY_2_LE), + bax: Fq::from(PAIRING_BAX_2_LE), + bay: Fq::from(PAIRING_BAY_2_LE), + bbx: Fq::from(PAIRING_BBX_2_LE), + bby: Fq::from(PAIRING_BBY_2_LE), + }, + ]; + + assert_eq!(altbn128::alt_bn128_pairing(&all_pairs), Ok(true)); +} + +fn test_pairing_no_input() { + assert_eq!(alt_bn128_pairing_raw(&[]), Ok(true)); +} + +fn test_pairing_invalid_a() { + let pair = Pair { + ax: Fq::from([0x11u8; 32]), + ay: Fq::from([0x11u8; 32]), + bax: Fq::from([0x11u8; 32]), + bay: Fq::from([0x11u8; 32]), + bbx: Fq::from([0x11u8; 32]), + bby: Fq::from([0x11u8; 32]), + }; + + assert_eq!(altbn128::alt_bn128_pairing(&[pair]), Err(Error::InvalidA)); +} +fn test_pairing_on_curve() { + let pair = Pair { + ax: Fq::from([0; 32]), + ay: Fq::from([0; 32]), + bax: Fq::from([0; 32]), + bay: Fq::from([0; 32]), + bbx: Fq::from([0; 32]), + bby: Fq::from([0; 32]), + }; + + assert_eq!(altbn128::alt_bn128_pairing(&[pair]), Ok(true),); +} + +fn test_alt_bn128_invalid_pairing_args() { + assert_eq!(alt_bn128_pairing_raw(&[0u8; 0]), Ok(true)); + assert_eq!(alt_bn128_pairing_raw(&[0u8; 1]), Err(Error::InvalidLength)); + assert_eq!( + unsafe { + let mut result = MaybeUninit::uninit(); + ext_ffi::casper_alt_bn128_pairing( + [0u8; 0].as_ptr() as *const c_void, + 193, + result.as_mut_ptr(), + ) + }, + 1 + ); +} + +pub(crate) fn perform_tests() { + test_alt_bn128_add(); + test_alt_bn128_mul(); + test_alt_bn128_pairing(); + + test_alt_bn128_invalid_pairing_args(); + test_zero_add(); + test_add_error(); + test_zero_multiplication(); + test_not_on_curve_multiplication(); + test_pairing_no_input(); + test_pairing_invalid_a(); + test_pairing_on_curve(); +} diff --git a/smart_contracts/contracts/test/altbn128/src/main.rs b/smart_contracts/contracts/test/altbn128/src/main.rs new file mode 100644 index 0000000000..5689f301ec --- /dev/null +++ b/smart_contracts/contracts/test/altbn128/src/main.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[cfg(not(any( + feature = "wasm_add_test", + feature = "wasm_mul_test", + feature = "wasm_pairing_test" +)))] +mod host; +#[cfg(not(any( + feature = "wasm_add_test", + feature = "wasm_mul_test", + feature = "wasm_pairing_test" +)))] +use host::perform_tests; +#[cfg(any( + feature = "wasm_add_test", + feature = "wasm_mul_test", + feature = "wasm_pairing_test" +))] +mod wasm; +#[cfg(any( + feature = "wasm_add_test", + feature = "wasm_mul_test", + feature = "wasm_pairing_test" +))] +use wasm::perform_tests; + +#[no_mangle] +pub extern "C" fn call() { + perform_tests(); +} diff --git a/smart_contracts/contracts/test/altbn128/src/wasm.rs b/smart_contracts/contracts/test/altbn128/src/wasm.rs new file mode 100644 index 0000000000..ee604bc688 --- /dev/null +++ b/smart_contracts/contracts/test/altbn128/src/wasm.rs @@ -0,0 +1,254 @@ +//! Consts for wasm-version of a bn curves. +//! +//! Counter-intuitively, these values are encoded in big-endian form as the bn crate expects u256 +//! integers to be encoded in big-endian form. +//! +//! The host version of the bn crate expects u256 integers to be encoded in little-endian form as +//! it's more natural and expectedly more efficient for of transforming u256 values to and from +//! host. +#[allow(unused_imports)] +use alloc::vec::Vec; +#[allow(unused_imports)] +use bn::{AffineG1, AffineG2, Fq, Fr, Group, G1}; +#[allow(unused_imports)] +use casper_contract::contract_api::builtins::altbn128::Error; + +#[allow(dead_code)] +mod consts { + pub const ADD_X1_BE: [u8; 32] = [ + 24, 177, 138, 207, 180, 194, 195, 2, 118, 219, 84, 17, 54, 142, 113, 133, 179, 17, 221, 18, + 70, 145, 97, 12, 93, 59, 116, 3, 78, 9, 61, 201, + ]; + pub const ADD_Y1_BE: [u8; 32] = [ + 6, 60, 144, 156, 71, 32, 132, 12, 181, 19, 76, 185, 245, 159, 167, 73, 117, 87, 150, 129, + 150, 88, 211, 46, 252, 13, 40, 129, 152, 243, 114, 102, + ]; + pub const ADD_X2_BE: [u8; 32] = [ + 7, 194, 183, 245, 138, 132, 189, 97, 69, 240, 12, 156, 43, 192, 187, 26, 24, 127, 32, 255, + 44, 146, 150, 58, 136, 1, 158, 124, 106, 1, 78, 237, + ]; + + pub const ADD_Y2_BE: [u8; 32] = [ + 6, 97, 78, 32, 193, 71, 233, 64, 242, 215, 13, 163, 247, 76, 154, 23, 223, 54, 23, 6, 164, + 72, 92, 116, 43, 214, 120, 132, 120, 250, 23, 215, + ]; + + pub const ADD_EXPECTED_X_BE: [u8; 32] = [ + 34, 67, 82, 92, 94, 253, 75, 156, 61, 60, 69, 172, 12, 163, 254, 77, 216, 94, 131, 10, 76, + 230, 182, 95, 161, 238, 174, 226, 2, 131, 151, 3, + ]; + pub const ADD_EXPECTED_Y_BE: [u8; 32] = [ + 48, 29, 29, 51, 190, 109, 168, 229, 9, 223, 33, 204, 53, 150, 71, 35, 24, 14, 237, 117, 50, + 83, 125, 185, 174, 94, 125, 72, 241, 149, 201, 21, + ]; + + pub const MUL_X_BE: [u8; 32] = [ + 43, 211, 230, 208, 243, 177, 66, 146, 79, 92, 167, 180, 156, 229, 185, 213, 76, 71, 3, 215, + 174, 86, 72, 230, 29, 2, 38, 139, 26, 10, 159, 183, + ]; + pub const MUL_Y_BE: [u8; 32] = [ + 33, 97, 28, 224, 166, 175, 133, 145, 94, 47, 29, 112, 48, 9, 9, 206, 46, 73, 223, 173, 74, + 70, 25, 200, 57, 12, 174, 102, 206, 253, 178, 4, + ]; + pub const MUL_SCALAR_BE: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 19, 140, 231, + 80, 250, 21, 194, + ]; + pub const MUL_EXPECTED_X_BE: [u8; 32] = [ + 7, 10, 141, 106, 152, 33, 83, 202, 228, 190, 41, 212, 52, 232, 250, 239, 138, 71, 178, 116, + 160, 83, 245, 164, 238, 42, 108, 156, 19, 195, 30, 92, + ]; + pub const MUL_EXPECTED_Y_BE: [u8; 32] = [ + 3, 27, 140, 233, 20, 235, 163, 169, 255, 185, 137, 249, 205, 213, 176, 240, 25, 67, 7, 75, + 244, 240, 243, 21, 105, 14, 195, 206, 198, 152, 26, 252, + ]; + + pub const PAIRING_AX_1_BE: [u8; 32] = [ + 28, 118, 71, 111, 77, 239, 75, 185, 69, 65, 213, 126, 187, 161, 25, 51, 129, 255, 167, 170, + 118, 173, 166, 100, 221, 49, 193, 96, 36, 196, 63, 89, + ]; + pub const PAIRING_AY_1_BE: [u8; 32] = [ + 48, 52, 221, 41, 32, 246, 115, 226, 4, 254, 226, 129, 28, 103, 135, 69, 252, 129, 155, 85, + 211, 233, 210, 148, 228, 92, 155, 3, 167, 106, 239, 65, + ]; + pub const PAIRING_BAX_1_BE: [u8; 32] = [ + 32, 157, 209, 94, 191, 245, 212, 108, 75, 216, 136, 229, 26, 147, 207, 153, 167, 50, 150, + 54, 198, 53, 20, 57, 107, 74, 69, 32, 3, 163, 91, 247, + ]; + pub const PAIRING_BAY_1_BE: [u8; 32] = [ + 4, 191, 17, 202, 1, 72, 59, 250, 139, 52, 180, 53, 97, 132, 141, 40, 144, 89, 96, 17, 76, + 138, 192, 64, 73, 175, 75, 99, 21, 164, 22, 120, + ]; + pub const PAIRING_BBX_1_BE: [u8; 32] = [ + 43, 184, 50, 74, 246, 207, 201, 53, 55, 162, 173, 26, 68, 92, 253, 12, 162, 167, 26, 205, + 122, 196, 31, 173, 191, 147, 60, 42, 81, 190, 52, 77, + ]; + pub const PAIRING_BBY_1_BE: [u8; 32] = [ + 18, 10, 42, 76, 243, 12, 27, 249, 132, 95, 32, 198, 254, 57, 224, 126, 162, 204, 230, 31, + 12, 155, 176, 72, 22, 95, 229, 228, 222, 135, 117, 80, + ]; + pub const PAIRING_AX_2_BE: [u8; 32] = [ + 17, 30, 18, 159, 28, 241, 9, 119, 16, 212, 28, 74, 199, 15, 205, 250, 91, 162, 2, 60, 111, + 241, 203, 234, 195, 34, 222, 73, 209, 182, 223, 124, + ]; + pub const PAIRING_AY_2_BE: [u8; 32] = [ + 32, 50, 198, 26, 131, 14, 60, 23, 40, 109, 233, 70, 43, 242, 66, 252, 162, 136, 53, 133, + 185, 56, 112, 167, 56, 83, 250, 206, 106, 107, 244, 17, + ]; + pub const PAIRING_BAX_2_BE: [u8; 32] = [ + 25, 142, 147, 147, 146, 13, 72, 58, 114, 96, 191, 183, 49, 251, 93, 37, 241, 170, 73, 51, + 53, 169, 231, 18, 151, 228, 133, 183, 174, 243, 18, 194, + ]; + pub const PAIRING_BAY_2_BE: [u8; 32] = [ + 24, 0, 222, 239, 18, 31, 30, 118, 66, 106, 0, 102, 94, 92, 68, 121, 103, 67, 34, 212, 247, + 94, 218, 221, 70, 222, 189, 92, 217, 146, 246, 237, + ]; + pub const PAIRING_BBX_2_BE: [u8; 32] = [ + 9, 6, 137, 208, 88, 95, 240, 117, 236, 158, 153, 173, 105, 12, 51, 149, 188, 75, 49, 51, + 112, 179, 142, 243, 85, 172, 218, 220, 209, 34, 151, 91, + ]; + pub const PAIRING_BBY_2_BE: [u8; 32] = [ + 18, 200, 94, 165, 219, 140, 109, 235, 74, 171, 113, 128, 141, 203, 64, 143, 227, 209, 231, + 105, 12, 67, 211, 123, 76, 230, 204, 1, 102, 250, 125, 170, + ]; +} +use consts::*; + +#[cfg(feature = "wasm_add_test")] +fn wasm_add_test() { + let p1 = bn_point_from_coords(&ADD_X1_BE, &ADD_Y1_BE); + let p2 = bn_point_from_coords(&ADD_X2_BE, &ADD_Y2_BE); + + let mut x = None; + let mut y = None; + + if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { + x = Some(sum.x()); + y = Some(sum.y()); + } + + assert_eq!( + (x, y), + ( + Some(Fq::from_slice(&ADD_EXPECTED_X_BE).unwrap()), + Some(Fq::from_slice(&ADD_EXPECTED_Y_BE).unwrap()) + ) + ); +} +#[allow(dead_code)] +fn bn_point_from_coords(x: &[u8; 32], y: &[u8; 32]) -> G1 { + let px = Fq::from_slice(x).unwrap(); + let py = Fq::from_slice(y).unwrap(); + + if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py).unwrap().into() + } +} +#[cfg(feature = "wasm_mul_test")] +fn wasm_mul_test() { + fn bn_fq_to_be_bytes(bn_fq: Fq) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bn_fq.to_big_endian(&mut bytes).unwrap(); + bytes + } + + let p = bn_point_from_coords(&MUL_X_BE, &MUL_Y_BE); + + let mut x = None; + let mut y = None; + let fr = Fr::from_slice(&MUL_SCALAR_BE).unwrap(); + + if let Some(product) = AffineG1::from_jacobian(p * fr) { + x = Some(bn_fq_to_be_bytes(product.x())); + y = Some(bn_fq_to_be_bytes(product.y())); + } + + assert_eq!((x, y), (Some(MUL_EXPECTED_X_BE), Some(MUL_EXPECTED_Y_BE))); +} + +pub fn perform_tests() { + // Wasm-only tests (compile BN crate to Wassm) + #[cfg(feature = "wasm_add_test")] + { + wasm_add_test(); + } + + #[cfg(feature = "wasm_mul_test")] + { + wasm_mul_test(); + } + + #[cfg(feature = "wasm_pairing_test")] + { + wasm_pairing_test(); + } +} + +#[cfg(feature = "wasm_pairing_test")] +pub fn wasm_alt_bn128_pairing( + values: &[(&[u8], &[u8], &[u8], &[u8], &[u8], &[u8])], +) -> Result { + let mut pairs = Vec::with_capacity(values.len()); + + for (ax, ay, bay, bax, bby, bbx) in values { + let ax = Fq::from_slice(&ax).map_err(|_| Error::InvalidAx)?; + let ay = Fq::from_slice(&ay).map_err(|_| Error::InvalidAy)?; + let bay = Fq::from_slice(&bay).map_err(|_| Error::InvalidBay)?; + let bax = Fq::from_slice(&bax).map_err(|_| Error::InvalidBax)?; + let bby = Fq::from_slice(&bby).map_err(|_| Error::InvalidBby)?; + let bbx = Fq::from_slice(&bbx).map_err(|_| Error::InvalidBbx)?; + + let g1_a = { + if ax.is_zero() && ay.is_zero() { + bn::G1::zero() + } else { + bn::AffineG1::new(ax, ay) + .map_err(|_| Error::InvalidA)? + .into() + } + }; + let g1_b = { + let ba = bn::Fq2::new(bax, bay); + let bb = bn::Fq2::new(bbx, bby); + + if ba.is_zero() && bb.is_zero() { + bn::G2::zero() + } else { + bn::AffineG2::new(ba, bb) + .map_err(|_| Error::InvalidB)? + .into() + } + }; + + pairs.push((g1_a, g1_b)); + } + + Ok(bn::pairing_batch(pairs.as_slice()) == bn::Gt::one()) +} + +#[cfg(feature = "wasm_pairing_test")] +fn wasm_pairing_test() { + assert_eq!( + wasm_alt_bn128_pairing(&[ + ( + &PAIRING_AX_1_BE, + &PAIRING_AY_1_BE, + &PAIRING_BAX_1_BE, + &PAIRING_BAY_1_BE, + &PAIRING_BBX_1_BE, + &PAIRING_BBY_1_BE + ), + ( + &PAIRING_AX_2_BE, + &PAIRING_AY_2_BE, + &PAIRING_BAX_2_BE, + &PAIRING_BAY_2_BE, + &PAIRING_BBX_2_BE, + &PAIRING_BBY_2_BE + ), + ]), + Ok(true) + ); +} diff --git a/types/src/chainspec/vm_config/host_function_costs.rs b/types/src/chainspec/vm_config/host_function_costs.rs index cf02f007c3..da42b33ad5 100644 --- a/types/src/chainspec/vm_config/host_function_costs.rs +++ b/types/src/chainspec/vm_config/host_function_costs.rs @@ -97,6 +97,9 @@ pub const DEFAULT_COST_INCREASE_PER_MESSAGE_EMITTED: u32 = 50; const DEFAULT_MESSAGE_TOPIC_NAME_SIZE_WEIGHT: u32 = 30_000; const DEFAULT_MESSAGE_PAYLOAD_SIZE_WEIGHT: u32 = 120_000; +const DEFAULT_ALT_BN128_ADD_COST: u32 = 1_000_000; +const DEFAULT_ALT_BN128_MUL_COST: u32 = 1_000_000; +const DEFAULT_ALT_BN128_PAIRING_COST: u32 = 1_000_000; /// Representation of a host function cost. /// @@ -337,6 +340,12 @@ pub struct HostFunctionCosts { pub manage_message_topic: HostFunction<[Cost; 4]>, /// Cost of calling the `casper_emit_message` host function. pub emit_message: HostFunction<[Cost; 4]>, + /// Cost of calling the `alt_bn128_add` host function. + pub alt_bn128_add: HostFunction<[Cost; 6]>, + /// Cost of calling the `alt_bn128_mul` host function. + pub alt_bn128_mul: HostFunction<[Cost; 5]>, + /// Cost of calling the `alt_bn128_pairing` host function. + pub alt_bn128_pairing: HostFunction<[Cost; 3]>, } impl Zero for HostFunctionCosts { @@ -390,6 +399,9 @@ impl Zero for HostFunctionCosts { manage_message_topic: HostFunction::zero(), emit_message: HostFunction::zero(), cost_increase_per_message: Zero::zero(), + alt_bn128_add: HostFunction::zero(), + alt_bn128_mul: HostFunction::zero(), + alt_bn128_pairing: HostFunction::zero(), } } @@ -443,6 +455,9 @@ impl Zero for HostFunctionCosts { enable_contract_version, manage_message_topic, emit_message, + alt_bn128_add, + alt_bn128_mul, + alt_bn128_pairing, } = self; read_value.is_zero() && dictionary_get.is_zero() @@ -492,6 +507,9 @@ impl Zero for HostFunctionCosts { && emit_message.is_zero() && cost_increase_per_message.is_zero() && add_package_version.is_zero() + && alt_bn128_add.is_zero() + && alt_bn128_mul.is_zero() + && alt_bn128_pairing.is_zero() } } @@ -679,6 +697,18 @@ impl Default for HostFunctionCosts { ], ), cost_increase_per_message: DEFAULT_COST_INCREASE_PER_MESSAGE_EMITTED, + alt_bn128_add: HostFunction::new( + DEFAULT_ALT_BN128_ADD_COST, + [NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED], + ), + alt_bn128_mul: HostFunction::new( + DEFAULT_ALT_BN128_MUL_COST, + [NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED], + ), + alt_bn128_pairing: HostFunction::new( + DEFAULT_ALT_BN128_PAIRING_COST, + [NOT_USED, DEFAULT_ARG_CHARGE, NOT_USED], + ), } } } @@ -734,6 +764,9 @@ impl ToBytes for HostFunctionCosts { ret.append(&mut self.manage_message_topic.to_bytes()?); ret.append(&mut self.emit_message.to_bytes()?); ret.append(&mut self.cost_increase_per_message.to_bytes()?); + ret.append(&mut self.alt_bn128_add.to_bytes()?); + ret.append(&mut self.alt_bn128_mul.to_bytes()?); + ret.append(&mut self.alt_bn128_pairing.to_bytes()?); Ok(ret) } @@ -786,6 +819,9 @@ impl ToBytes for HostFunctionCosts { + self.manage_message_topic.serialized_length() + self.emit_message.serialized_length() + self.cost_increase_per_message.serialized_length() + + self.alt_bn128_add.serialized_length() + + self.alt_bn128_mul.serialized_length() + + self.alt_bn128_pairing.serialized_length() } } @@ -839,6 +875,9 @@ impl FromBytes for HostFunctionCosts { let (manage_message_topic, rem) = FromBytes::from_bytes(rem)?; let (emit_message, rem) = FromBytes::from_bytes(rem)?; let (cost_increase_per_message, rem) = FromBytes::from_bytes(rem)?; + let (alt_bn128_add, rem) = FromBytes::from_bytes(rem)?; + let (alt_bn128_mul, rem) = FromBytes::from_bytes(rem)?; + let (alt_bn128_pairing, rem) = FromBytes::from_bytes(rem)?; Ok(( HostFunctionCosts { read_value, @@ -889,6 +928,9 @@ impl FromBytes for HostFunctionCosts { manage_message_topic, emit_message, cost_increase_per_message, + alt_bn128_add, + alt_bn128_mul, + alt_bn128_pairing, }, rem, )) @@ -947,6 +989,9 @@ impl Distribution for Standard { manage_message_topic: rng.gen(), emit_message: rng.gen(), cost_increase_per_message: rng.gen(), + alt_bn128_add: rng.gen(), + alt_bn128_mul: rng.gen(), + alt_bn128_pairing: rng.gen(), } } } @@ -1014,6 +1059,9 @@ pub mod gens { manage_message_topic in host_function_cost_arb(), emit_message in host_function_cost_arb(), cost_increase_per_message in num::u32::ANY, + alt_bn128_add in host_function_cost_arb(), + alt_bn128_mul in host_function_cost_arb(), + alt_bn128_pairing in host_function_cost_arb(), ) -> HostFunctionCosts { HostFunctionCosts { read_value, @@ -1064,6 +1112,9 @@ pub mod gens { manage_message_topic, emit_message, cost_increase_per_message, + alt_bn128_add, + alt_bn128_mul, + alt_bn128_pairing, } } } diff --git a/types/src/uint.rs b/types/src/uint.rs index bdb30a45a5..912b7ebc67 100644 --- a/types/src/uint.rs +++ b/types/src/uint.rs @@ -43,6 +43,7 @@ mod macro_code { pub struct U512(8); } construct_uint! { + #[cfg_attr(feature = "datasize", derive(DataSize))] pub struct U256(4); } @@ -68,6 +69,38 @@ pub enum UIntParseError { macro_rules! impl_traits_for_uint { ($type:ident, $total_bytes:expr, $test_mod:ident) => { + impl $type { + /// Return the memory representation of this integer as a byte array in little-endian + /// byte order. + pub fn to_le_bytes(self) -> [u8; $total_bytes] { + let mut result = [0u8; $total_bytes]; + self.to_little_endian(&mut result); + result + } + + /// Return the memory representation of this integer as a byte array in big-endian byte + /// order. + pub fn to_be_bytes(self) -> [u8; $total_bytes] { + let mut result = [0u8; $total_bytes]; + self.to_big_endian(&mut result); + result + } + + /// Create a native endian integer value from its representation as a byte array in + /// little endian. + #[inline(always)] + pub fn from_le_bytes(bytes: [u8; $total_bytes]) -> Self { + Self::from_little_endian(&bytes) + } + + /// Create a native endian integer value from its representation as a byte array in big + /// endian. + #[inline(always)] + pub fn from_be_bytes(bytes: [u8; $total_bytes]) -> Self { + Self::from_big_endian(&bytes) + } + } + impl Serialize for $type { fn serialize(&self, serializer: S) -> Result { if serializer.is_human_readable() {