From d5cafd73a7eb1076ed1503a0279dc12648a7571e Mon Sep 17 00:00:00 2001 From: Olga Kunyavskaya Date: Sat, 24 Dec 2022 10:53:26 +0200 Subject: [PATCH] nep-0446: bls-signature verifiaction --- neps/nep-0446.md | 168 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 neps/nep-0446.md diff --git a/neps/nep-0446.md b/neps/nep-0446.md new file mode 100644 index 000000000..f17866d65 --- /dev/null +++ b/neps/nep-0446.md @@ -0,0 +1,168 @@ +--- +NEP: 446 +Title: Efficient BLS-signature verification +Author: Olga Kuniavskaia +DiscussionsTo: https://github.com/nearprotocol/neps/pull/446 +Status: Draft +Type: Runtime Spec +Category: Contract +Created: 24-Dec-2022 +--- + +## Summary + +A pre-compiled NEAR runtime function for verifying BLS12-381 signatures. +That enables running Ethereum 2 light clients on-chain. + +## Motivation + +For proper work of the Rainbow Bridge, the ETH2 Light Client should be implemented +as a NEAR contract. It has to verify +BLS12-381 signatures. At the moment, there are no practical implementations +of BLS signatures that (1) have been audited, (2) could be compiled into WebAssembly, +and (3) would fit into the gas limit. + +Hence, verification of BLS signatures +in the Rainbow Bridge is currently performed off-chain, which makes it not trustless, +as we would like. This proposal adds the ability +to verify the aggregated BLS12-381 signature to the NEAR Runtime level. +This NEP makes verifing ETH2 data completely on NEAR possible, which makes the Rainbow Bridge +trustless. + +## Rationale and alternatives + +One of the core parts of Rainbow Bridge is the Ethereum Light client implemented on NEAR. +The ETH2 Light Client has to verify the Light Client Update and, as a part of that, +the BLS signature. + +The main alternative is to use an existing BLS verification library inside a smart contract, +which requires no changes to NEAR. + +Following Rust libraries for the BLS signature verification are available. + +* [BLST](https://github.com/supranational/blst) (used by Lighthouse by default), Rust bindings to C-library implementation by Supranational team. Has some audits, some are in progress. + Has the best performance, but cannot be compiled to WebAssembly and cannot be embedded into a smart contract. +* [sigp/milagro-bls](https://github.com/sigp/milagro_bls). Implemented by SigmaPrime, uses The Apache Milagro Cryptographic Library. Optionally could be enabled in LightHouse. Not Audited. + The BLS signature verification is very gas-consuming. In our tests it has exceeded the NEAR per-contract function gas limit + dozens of times over. +* [filecoin/bls-signatures](https://github.com/filecoin-project/bls-signatures). Implementation of BLS signatures in pure Rust. Not Audited. +* [zkcrypto/bls12-381](https://github.com/zkcrypto/bls12_381) (zk-crypto implementation. By ZCash developers) Not Audited. + +Alternatively, one could consider trying to split the signature verification function into multiple iterations, +or developing a faster, smart contract-compatible implementation on our own. +However, such development require low-level changes in the bls-verification libraries and long-term audit in order +to comply with all security guidelines. + +The other alternative is execution signature verification off-chain using multiple validators and multisigs. +This solution currently implement in the Rainbow Bridge. +However, in this case, Rainbow Bridge can't be considered as trustless. + +This NEP proposes to make the BLS-signature verification as NEAR runtime pre-compiled function. +In that case we can use the fast and audited BLST library. +Our calculation shows that the per-contract function gas limit is not exceeded in this case. + +## Specification + +This NEP introduces the following host function: + +```rust +extern "C" { + /// Verify the given BLS12-381 signature for the given + /// message and either a non-empty public keys list or an aggregated public key. + /// + /// A correct signature is a sequence of 96 bytes. + /// + /// The message is a possibly empty sequence of arbitrary bytes. + /// + /// If a list of public keys is passed, all keys should be written consecutively + /// without any separation. Each should is a sequence of 48 bytes, hence + /// a list of N keys should be 48*N bytes long. + /// + /// If an aggregated public key is used instead, it should be passed alone. + /// The length of an aggregated public key is always 48 bytes as well. + /// + /// Returns 1 if the signature verification pass, and 0 if the underlying + /// `BLST` library returns `BLST_VERIFY_FAIL`. + /// + /// # Errors + /// + /// Contract execution is terminated with an error if either: + /// + /// * Any passed arrays are out of memory bounds + /// * The underlying library returns any error except `BLST_VERIFY_FAIL`. + /// E.g. the signature is invalid regardless of the message. + /// + /// # Cost + /// + /// `input_cost(aggregate_signature_len) + input_cost(msg_len) + input_cost(pubkeys_len) + + /// bls12381_verify_base + bls12381_verify_byte * msg_len + bls12381_verify_elements * pubkeys_cnt` + /// + /// Here `pubkeys_cnt` is the number of public keys (`pubkeys_len / 48`). + /// + /// BLS12-381 allows aggregating all public keys provided into a single key equivalent to the list. + /// That way the number of public keys and the message length influence the cost independently. + fn bls12_381_aggregate_verify( + aggregate_signature_ptr: u64, + aggregate_signature_len: u64, + msg_ptr: u64, + msg_len: u64, + pubkeys_ptr: u64, + pubkeys_len: u64, + ) -> u64; +} +``` + +A `rust-sdk` possible implementation could look like this: + +```rs +pub fn bls12_381_aggregate_verify(aggregate_signature: &[u8], msg: &[u8], pubkeys: &[u8]) -> u64; +``` + +Once this NEP is approved and integrated, these functions will be available in the `near_sdk` crate in the +`env` module. + +The main goal is to be able to check the sync committee signature of light client update for Ethereum 2. +For example: +```rust +near_sdk::env::bls12_381_aggregate_verify( + &light_client_update + .sync_aggregate + .sync_committee_signature.0, + &signing_root.0.as_bytes(), + &pubkeys +); +``` +Here `signing_root` is the tree hash of `light_client_update.attested_beacon_header` and `pubkeys` +is the concatenation of the subset of `sync_committee.pubkeys`. +Specifically, only pubkeys which signed light client update. +In other words, pubkeys with a corresponding bit in `light_client_update.sync_aggregate.sync_committee_bits` set to 1. + +For verification inside `bls12_381_aggregate_verify` function [the BLST library](https://github.com/supranational/blst) is used, +specifically the `fast_aggregate_verify`. The PR with implementation is available at https://github.com/near/nearcore/pull/8184 + +## Security Implications (Optional) + +The security of a function depends on the security of the library used. +We suggest using [the BLST library](https://github.com/supranational/blst). This library were audited and shows the good performance. + +## Unresolved issues + +1. The verification can fail in two ways: either err or return 0. + The distinction between the two is unclear, and the goal of such distinction is unclear as well. + BLST library has lots of possible verification fail causes, and we call it multiple times, which should be what? +2. It may be useful to aggregate a list of public keys once and use it later for multiple verifications. + This improves performance. + Maybe add a separate aggregation function and make the current function accept only a single key (either usual or aggregated). + +## Future possibilities + +The Ethereum 2 supports even more low-level precompiled operations for BLS12-381, +see [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537). +In the future it can be useful to support these operations on NEAR as well to be able +effectively execute Ethereum 2 contracts on NEAR. + +## Copyright + +[copyright]: #copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).