Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

[Unreleased]

### Added
- Implement `bn128` precompiles ‒ [2708](https://github.com/use-ink/ink/pull/2718)

### Changed
- Refactor contract ref generation and add automatic re-exporting - [#2710](https://github.com/use-ink/ink/pull/2710)
- Refactor contract ref generation and add automatic re-exporting [#2710](https://github.com/use-ink/ink/pull/2710)
- Implement and stabilize `terminate_contract` ‒ [2708](https://github.com/use-ink/ink/pull/2708)

## Version 6.0.0-beta
Expand Down Expand Up @@ -2640,4 +2643,4 @@ impl Contract {

This is useful if the `impl` block itself does not contain any ink! constructors or messages, but you
still need to access some of the "magic" provided by ink!. In the example above, you would not have
access to `emit_event` without `#[ink(impl)]`.
access to `emit_event` without `#[ink(impl)]`.
21 changes: 21 additions & 0 deletions crates/engine/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,27 @@ impl Engine {
}

impl Engine {
/// Implements the `bn128_add` G1 addition precompile.
pub fn bn128_add(self, _x1: U256, _y1: U256, _x2: U256, _y2: U256) -> (U256, U256) {
unimplemented!(
"`bn128_add` is not implemented in the off-chain testing environment"
);
}

/// Implements the `bn128_mul` G1 scalar-mul precompile.
pub fn bn128_mul(self, _x1: U256, _y1: U256, _scalar: U256) -> (U256, U256) {
unimplemented!(
"`bn128_mul` is not implemented in the off-chain testing environment"
);
}

/// Implements the `bn128_pairing` precompile.
pub fn bn128_pairing(self, _input: &[u8]) -> bool {
unimplemented!(
"`bn128_pairing` is not implemented in the off-chain testing environment"
);
}

/// Recovers the compressed ECDSA public key for given `signature` and `message_hash`,
/// and stores the result in `output`.
#[allow(clippy::arithmetic_side_effects)] // todo
Expand Down
43 changes: 43 additions & 0 deletions crates/env/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,49 @@ where
})
}

/// Calls the `bn128_add` G1 addition precompile.
///
/// Inputs are affine G1 coordinates over Fq.
/// Returns the resulting affine point or (0, 0) if the result is ∞ / invalid.
///
/// # Note
///
/// The precompile address is `0x06`. You can find its implementation here:
/// <https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/precompiles/builtin/bn128.rs>
pub fn bn128_add(x1: U256, y1: U256, x2: U256, y2: U256) -> (U256, U256) {
<EnvInstance as OnInstance>::on_instance(|instance| {
instance.bn128_add(x1, y1, x2, y2)
})
}

/// Calls the `bn128_mul` G1 scalar-mul precompile.
///
/// Multiplies an affine G1 point (x1, y1) by a scalar ∈ Fr.
/// Returns the resulting affine point or (0, 0) if the result is ∞ / invalid.
///
/// # Note
///
/// The precompile address is `0x07`. You can find its implementation here:
/// <https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/precompiles/builtin/bn128.rs>
pub fn bn128_mul(x1: U256, y1: U256, scalar: U256) -> (U256, U256) {
<EnvInstance as OnInstance>::on_instance(|instance| {
instance.bn128_mul(x1, y1, scalar)
})
}

/// Calls the `bn128_pairing` precompile.
///
/// Input is the Solidity-ABI-encoded sequence of (G1, G2) pairs.
/// Returns `true` iff the product of pairings evaluates to the identity.
///
/// # Note
///
/// The precompile address is `0x08`. You can find its implementation here:
/// <https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/precompiles/builtin/bn128.rs>
pub fn bn128_pairing(input: &[u8]) -> bool {
<EnvInstance as OnInstance>::on_instance(|instance| instance.bn128_pairing(input))
}

/// Recovers the compressed ECDSA public key for given `signature` and `message_hash`,
/// and stores the result in `output`.
///
Expand Down
33 changes: 33 additions & 0 deletions crates/env/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,39 @@ pub trait EnvBackend {
H: CryptoHash,
T: scale::Encode;

/// Calls the `bn128_add` G1 addition precompile.
///
/// Inputs are affine G1 coordinates over Fq.
/// Returns the resulting affine point or (0, 0) if the result is ∞ / invalid.
///
/// # Note
///
/// The precompile address is `0x06`. You can find its implementation here:
/// <https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/precompiles/builtin/bn128.rs>
fn bn128_add(&mut self, x1: U256, y1: U256, x2: U256, y2: U256) -> (U256, U256);

/// Calls the `bn128_mul` G1 scalar-mul precompile.
///
/// Multiplies an affine G1 point (x1, y1) by a scalar ∈ Fr.
/// Returns the resulting affine point or (0, 0) if the result is ∞ / invalid.
///
/// # Note
///
/// The precompile address is `0x07`. You can find its implementation here:
/// <https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/precompiles/builtin/bn128.rs>
fn bn128_mul(&mut self, x1: U256, y1: U256, scalar: U256) -> (U256, U256);

/// Calls the `bn128_pairing` precompile.
///
/// Input is the Solidity-ABI-encoded sequence of (G1, G2) pairs.
/// Returns `true` iff the product of pairings evaluates to the identity.
///
/// # Note
///
/// The precompile address is `0x08`. You can find its implementation here:
/// <https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/revive/src/precompiles/builtin/bn128.rs>
fn bn128_pairing(&mut self, input: &[u8]) -> bool;

/// Recovers the compressed ECDSA public key for given `signature` and `message_hash`,
/// and stores the result in `output`.
fn ecdsa_recover(
Expand Down
18 changes: 18 additions & 0 deletions crates/env/src/engine/off_chain/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,24 @@ impl EnvBackend for EnvInstance {
<H as CryptoHash>::hash(enc_input, output)
}

fn bn128_add(&mut self, _x1: U256, _y1: U256, _x2: U256, _y2: U256) -> (U256, U256) {
unimplemented!(
"`bn128_add` is not implemented in the off-chain testing environment"
);
}

fn bn128_mul(&mut self, _x1: U256, _y1: U256, _scalar: U256) -> (U256, U256) {
unimplemented!(
"`bn128_mul` is not implemented in the off-chain testing environment"
);
}

fn bn128_pairing(&mut self, _input: &[u8]) -> bool {
unimplemented!(
"`bn128_pairing` is not implemented in the off-chain testing environment"
);
}

#[allow(clippy::arithmetic_side_effects)] // todo
fn ecdsa_recover(
&mut self,
Expand Down
74 changes: 74 additions & 0 deletions crates/env/src/engine/on_chain/pallet_revive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,80 @@ impl EnvBackend for EnvInstance {
<H as CryptoHash>::hash(enc_input, output)
}

fn bn128_add(&mut self, x1: U256, y1: U256, x2: U256, y2: U256) -> (U256, U256) {
const BN128_ADD: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000006");

let mut input = [0u8; 128];
input[..32].copy_from_slice(&x1.to_big_endian());
input[32..64].copy_from_slice(&y1.to_big_endian());
input[64..96].copy_from_slice(&x2.to_big_endian());
input[96..128].copy_from_slice(&y2.to_big_endian());

let mut output = [0u8; 64];
let _ = ext::call(
CallFlags::empty(),
&BN128_ADD,
u64::MAX, // `ref_time` to devote for execution. `u64::MAX` = all
u64::MAX, // `proof_size` to devote for execution. `u64::MAX` = all
&[u8::MAX; 32], // No deposit limit.
&U256::zero().to_little_endian(), // Value transferred to the contract.
&input[..],
Some(&mut &mut output[..]),
);
let x3 = U256::from_big_endian(&output[..32]);
let y3 = U256::from_big_endian(&output[32..64]);
(x3, y3)
}

fn bn128_mul(&mut self, x1: U256, y1: U256, scalar: U256) -> (U256, U256) {
const BN128_ADD: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000007");

let mut input = [0u8; 128];
input[..32].copy_from_slice(&x1.to_big_endian());
input[32..64].copy_from_slice(&y1.to_big_endian());
input[64..96].copy_from_slice(&scalar.to_big_endian());

let mut output = [0u8; 64];
let _ = ext::call(
CallFlags::empty(),
&BN128_ADD,
u64::MAX, // `ref_time` to devote for execution. `u64::MAX` = all
u64::MAX, // `proof_size` to devote for execution. `u64::MAX` = all
&[u8::MAX; 32], // No deposit limit.
&U256::zero().to_little_endian(), // Value transferred to the contract.
&input[..],
Some(&mut &mut output[..]),
);
let x2 = U256::from_big_endian(&output[..32]);
let y2 = U256::from_big_endian(&output[32..64]);
(x2, y2)
}

fn bn128_pairing(&mut self, input: &[u8]) -> bool {
const BN128_ADD: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000008");

let mut output = [0u8; 32];
let _ = ext::call(
CallFlags::empty(),
&BN128_ADD,
u64::MAX, // `ref_time` to devote for execution. `u64::MAX` = all
u64::MAX, // `proof_size` to devote for execution. `u64::MAX` = all
&[u8::MAX; 32], // No deposit limit.
&U256::zero().to_little_endian(), // Value transferred to the contract.
&input[..],
Some(&mut &mut output[..]),
);
if output[31] == 1 {
debug_assert_eq!(&output[..31], [0u8; 31]);
return true;
}
debug_assert_eq!(&output[..32], [0u8; 32]);
false
}

fn ecdsa_recover(
&mut self,
signature: &[u8; 65],
Expand Down
36 changes: 36 additions & 0 deletions crates/ink/src/env_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,42 @@ where
output
}

/// Calls the `bn128_add` G1 addition precompile.
///
/// Inputs are affine G1 coordinates over Fq.
/// Returns the resulting affine point or (0, 0) if the result is ∞ / invalid.
///
/// # Note
///
/// For more details visit: [`ink_env::bn128_add`]
pub fn bn128_add(self, x1: U256, y1: U256, x2: U256, y2: U256) -> (U256, U256) {
ink_env::bn128_add(x1, y1, x2, y2)
}

/// Calls the `bn128_mul` G1 scalar-mul precompile.
///
/// Multiplies an affine G1 point (x1, y1) by a scalar ∈ Fr.
/// Returns the resulting affine point or (0, 0) if the result is ∞ / invalid.
///
/// # Note
///
/// For more details visit: [`ink_env::bn128_mul`]
pub fn bn128_mul(self, x1: U256, y1: U256, scalar: U256) -> (U256, U256) {
ink_env::bn128_mul(x1, y1, scalar)
}

/// Calls the `bn128_pairing` precompile.
///
/// Input is the Solidity-ABI-encoded sequence of (G1, G2) pairs.
/// Returns `true` iff the product of pairings evaluates to the identity.
///
/// # Note
///
/// For more details visit: [`ink_env::bn128_pairing`]
pub fn bn128_pairing(self, input: &[u8]) -> bool {
ink_env::bn128_pairing(input)
}

/// Recovers the compressed ECDSA public key for given `signature` and `message_hash`,
/// and stores the result in `output`.
///
Expand Down
29 changes: 29 additions & 0 deletions integration-tests/internal/builtin-precompiles/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "builtin_precompiles"
description = "E2E tests for various builtin precompiles"
version = "6.0.0-beta"
authors = ["Use Ink <[email protected]>"]
edition = "2021"
publish = false

[dependencies]
ink = { path = "../../../crates/ink", default-features = false }

[dev-dependencies]
ink_e2e = { path = "../../../crates/e2e" }
hex-literal = "1"
impl-serde = { version = "0.5.0", default-features = false }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
]
ink-as-dependency = []
e2e-tests = []

[package.metadata.ink-lang]
abi = "ink"
Loading
Loading