-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Conversation
I'll remain stuck doing something else most of this week, but I'll dig into this soon! Answering your initial questions though: I'd name it bandersnatch_vrf or bandersnatch_rvrf myself. We've no need for a short primitive name here since our usage of sergey's ring_proof is kinda niche: We update a relatively small 1000 node ring every 6 hours. Also, ring_proof closely resembles the beefy bridge work. We'll never put this primitive into a wallet I think. Although sr25519's VRF is safe to use with soft derivation, we cannot safely do soft derivations in bandersnatch_vrf because our ring VRF optimizations prevent hashing the public key like schnorrkel does. It's not quite as bad as how soft derivations break BLS signature entirely, but they'd break the VRF properties. At a high level, soft derivations are kinda an artifact of the UTXO model, which seems largely deprecated outside privacy coins. And legacy stuff like bitcoin. We do them in sr25519 because we can do so without messing up anything more important. We want a siloed hard delegation scheme instead of key derivations for the more scalable identity ring VRF planned for wallets, but this serves a different purpose: Allow different dApps to use a perfectly deterministically derived ring VRF key, without continually accessing your air gapped wallet, and also without any dApp learning the keys delegated to other dApps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks Good!
// 2048 → 295 KB | ||
// NOTE: This is quite big but looks like there is an upcoming fix | ||
// in the backend. | ||
const RING_CONTEXT_SERIALIZED_LEN: usize = 147752; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe DQ, but how did you come up with this max size?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skunert Good question. I was wondering this myself. (:
If this is supposed to be 147KB because the max supported ring size is 1024 then it's either too small (147 * 1024 = 150528) or too big (147 * 1000 = 147000) or the serialized sizes in the comment are just approximations. Maybe @davxy you could add an extra comment how this was calculated and why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the SCALE encoded size of the KZG
backend.
The w3f guys told be that probably this size can be reduced. But I'll keep this as an internal detail and at the current stage it doesn't matter a lot
} | ||
|
||
/// Create challenge from the transcript contained within the signing data. | ||
pub fn challenge<const N: usize>(&self) -> [u8; N] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good question. And I'm glad you asked so I can share something I hope is useful.
To understand the trivial answer some context is required.
Once upon a time Micali et al came out with a paper presenting Interactive Proof Systems (aka IP).
The idea of an IP system is that is able to prove a statement by sharing ~zero knowledge (well, just one bit of knowledge: true/false). I.e. prove if a statement is true without sharing any additional information (for example not sharing why a statement is true). This is in contrast with traditional proofs (think about math proofs where you give a proof as a set of logical steps).
Babai came out with what are called Arthur-Merlin protocols (aka AM). Quite similar but not identical to IP (e.g. randomness of verifier can be shared with the prover).
Has been proven that what can be proven via an IC can proved via a AM protocol (and vice versa)
AM and IP protocols are composed of an exchange of messages (challenges from verifier, responses from the prover) between two parties.
In what is called random-oracle model we assume that there exist some ideal hash function indistinguishable from a real randomness source. In practice we just use some well known hash function (e.g. SHA256).
In this model the challenge that in AM or IM come from the Verifier is replaced by an "auto-generated" challenge that is a function (hash) of what you want to prove (what is called Fiat-Shamir transform).
You can generate a set of challenge/responses. I.e. each challenge will be the some hash of the prev response.
Is assumed that the Prover is not in control of what is the output of the hash. I.e. indistinguishable from what would be a challenge from a "real" Verifier.
In this way we go from an interactive ZK proof sys to a non interactive ZK proofs.
This challenge()
can be used to get the computed challenge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is used by sassafras in the slot claim step
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm. my review of the meaty part of this PR (primitives/core/src/bandersnatch.rs
) is very light since I'm not too familiar with the inner workings of bandersnatch. judging from previous reviews it seems that both Sergey and Jeff already validated it
primitives/keystore/src/lib.rs
Outdated
/// The signature is valid if the signing key is part of the ring from which | ||
/// the `RingProver` has been derived. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if the signing key isn't part of the ring?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a signature is produced with a key whose corresponding public key doesn't belong to the ring (aka a group of public keys) then nothing bad happens. However the signature is useless as it can't be verified using the ring verifier.
I added some docs and a test clarifying this in 2b2676b
/// | ||
/// The signature is valid if the signing key is part of the ring from which | ||
/// the `RingProver` has been derived. | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't a bandersnatch_ring_vrf_verify
primitive provided? Is it because it requires the creation of a RingVerifier
context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor things but looks mostly fine to me.
// 2048 → 295 KB | ||
// NOTE: This is quite big but looks like there is an upcoming fix | ||
// in the backend. | ||
const RING_CONTEXT_SERIALIZED_LEN: usize = 147752; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skunert Good question. I was wondering this myself. (:
If this is supposed to be 147KB because the max supported ring size is 1024 then it's either too small (147 * 1024 = 150528) or too big (147 * 1000 = 147000) or the serialized sizes in the comment are just approximations. Maybe @davxy you could add an extra comment how this was calculated and why?
impl Derive for Public {} | ||
|
||
impl sp_std::fmt::Debug for Public { | ||
#[cfg(feature = "std")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be feature = "serde"
, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll eventually modify this for the others primitives as well. Follow up PR
impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_LEN]> for Signature { | ||
fn unchecked_from(raw: [u8; SIGNATURE_SERIALIZED_LEN]) -> Self { | ||
Signature(raw) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... maybe a silly question, but why isn't this just simply From
?
To me UncheckedFrom
implies that there should be a TryFrom
which does some extra invariant checking, but the TryFrom
implementation doesn't check any extra invariants besides checking the size, which for a From<[u8; SIGNATURE_SERIALIZED_LEN]>
would be checked at compile time.
Are there plans to extend the TryFrom
so that it's actually checking something else besides that the size matches?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The crypto primitive implements the same traits implemented by the other crypto types and using the same strategies.
Nevertheless, you are right and indeed my plan is to give some full overhaul to the crypto traits and interfaces in another PR
} | ||
|
||
impl sp_std::fmt::Debug for Signature { | ||
#[cfg(feature = "std")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this feature gate necessary? This should also work in no_std, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll eventually modify this for the others primitives as well. Follow up PR
fn derive<Iter: Iterator<Item = DeriveJunction>>( | ||
&self, | ||
path: Iter, | ||
_seed: Option<Seed>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a silly question, but why is this argument ignored? Perhaps put a comment here and/or return an error if it is Some
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tools which uses the InspectKeyCmd
may end up calling (after some hopping) the from_string_with_seed which in turn will call pair.derive(junctions, seed)
.
With:
seed
the seed generated from the secret phrase according to bip39.- Junctions are the hard/soft junctions after the phrase
Is left to the implementation what to do with the seed during the derive
which has been already used to generate the pair itself.
The only implementation which uses it is sr25519
and frankly I don't know why it requires it.
But I don't find it of any usage for bandersnatch as to derive a subkey we use the junctions.
Returning an error is not an option since is called in the same way for all the possible pairs
Co-authored-by: Koute <koute@users.noreply.github.com>
bot merge |
Error: "Check reviews" status is not passing for paritytech/polkadot#7547 |
bot merge |
Waiting for commit status. |
This PR introduces a new kind of VRF which uses
bandersnatch-vrfs
backend.The primitive can operate in two modes:
The code introduced by this PR has been partially extracted from Sassafras PR where the primitive integration has been proven to fulfill the protocol requirements (PoC node provided within the sassafras PR).
The feature is still experimental and should not be used in production since the implementation and interface may still be subject to significant changes.
To experiment with the primitive the
bandersnatch-experimental
feature should be enabled.Open points:
ring-proof, fflonk and bandersnatch-vrfs dependencies
Currently we depend on version published on github of
bandersnatch-vrfs
crate (also indirectly for its dependencies: w3f fflonk, and ring-proof) .Waiting for the crates to be published on
crates.io
Step towards #11879
Superseeds #13974
Polkadot companion: paritytech/polkadot#7547
Cumulus companion: paritytech/cumulus#2938