-
Notifications
You must be signed in to change notification settings - Fork 2.6k
VRF refactory #13889
VRF refactory #13889
Conversation
We should rename At a high level, these transcript flavored hashers like merlin or now ark-transcript are safer interfaces than regular hash functions, largely by making it hard to fuck up internal domain separation. A priori I'd prefer if they were used more, not encapsulated more deeply. Anything opinionated brings messy-make-work-arounds though, so not really sure the best course there. I suspect We've superficial similarities between schnorrkel types and ring vrf types, but they've no common ground under the hood: merlin vs ark-transcript, dalek type wrappers vs arkworks type wrappers, etc. As an aside, a |
…r VrfSigner and VrfVerifier
As I wrote in the PR description I definitely will. But I would prefer doing that in a follow-up PR do don't mess-up too much stuff in a single PR.
IMO exposing crypto primitives libraries interfaces in a framework is not a good idea and almost always introduces some form of technical debt in the long term. May be good for quick prototyping but as a best engineering practice is better to keep the dependencies as buried as possible.
This (again IMO) doesn't justify adopting the same strategy in a framework.
I had a quick look at the This indeed make the type diverge from the one used by
I agree Once this goes upstream I start the integration of |
ark-transcript commits lengths after the bytes, not before. It's literally just shake128 with postfix lengths in big endian u32s. This makes it possible to stream bytes into the transcript, and only learn how much you streamed at the end. This is necessary to maximize compatibility with arkworks' polymorphism and serialization. We've no reason for an additional If you need to pass a partial transcript to a remote signer or whatever, then you accumulate the transcript in a |
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.
Nice cleanups!
/// Verify input data signature. | ||
fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; |
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.
/// Verify input data signature. | |
fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; | |
/// Verify input data signature. | |
#[must_use] | |
fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; |
primitives/core/src/sr25519.rs
Outdated
impl Deref for VrfTranscript { | ||
type Target = merlin::Transcript; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} |
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 we want to get rid of merlin
wouldn't it be better not to expose its type here and just add necessary wrappers in impl VrfTranscript
for whatever is accessed through this deref
?
(Also similar for other Deref
s which are going to leak the underlying type.)
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 planned but I prefer doing it in a follow up since it affects too much stuff on polkadot
Co-authored-by: Koute <koute@users.noreply.github.com>
MuSigInconsistent { musig_stage: Commitment, duplicate: true } => | ||
"Signature error: `MuSigInconsistent` at stage `Commitment` on duplicate".into(), | ||
MuSigInconsistent { musig_stage: Commitment, duplicate: false } => | ||
"Signature error: `MuSigInconsistent` at stage `Commitment` on not duplicate".into(), | ||
MuSigInconsistent { musig_stage: Reveal, duplicate: true } => | ||
"Signature error: `MuSigInconsistent` at stage `Reveal` on duplicate".into(), | ||
MuSigInconsistent { musig_stage: Reveal, duplicate: false } => | ||
"Signature error: `MuSigInconsistent` at stage `Reveal` on not duplicate".into(), | ||
MuSigInconsistent { musig_stage: Cosignature, duplicate: true } => | ||
"Signature error: `MuSigInconsistent` at stage `Cosignature` on duplicate".into(), | ||
MuSigInconsistent { musig_stage: Cosignature, duplicate: false } => | ||
"Signature error: `MuSigInconsistent` at stage `Cosignature` on not duplicate" |
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.
nit: Do we need to hand-write all combinations here? Could we use format! or sth similar?
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 thing is that we are using some functions returning schnorrkel
errors in no_std
environments (e.g. make_bytes). And the schnorrkel::Error
to_string will return a String
.
Even though is possible to use String
in no_std (because is exposed there via some other crates...) , I'm not sure we want to do that 🤔 . In general I see that frame pallets avoid using directly String
s.
primitives/core/src/sr25519.rs
Outdated
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] | ||
pub struct VrfSignature { | ||
/// The initial VRF configuration | ||
pub output: VrfOutput, | ||
/// The calculated VRF proof | ||
pub proof: VrfProof, | ||
} |
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.
Should we put some comment that this structure is part of header digest and its layout needs to be backward compatible?
It is not obvious (or my understanding is not correct).
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.
at this level (i.e. in the sr25519 module) this is not defined to be part of something. Is just the output of the vrf_sign
primitive.
The fact that babe is going to serialize it and requires a precise order is something that will be eventually catched by some test.
Indeed the same argument applies if we store them separated since babe relies on a VrfProof
encoding that comes from outside (i.e. the order is decided by the to_bytes()
method of the VrfProof
.).
So probably (for these structures that are supposed to be serialized to the wire) each protocol (as babe) should define some tests vectors to check for encoding assumptions, thus to double check that nothing is broken in the external dep.
I'm going to add some test vectors. Ty for the hint
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.
Agreed, some explicit test makes more sense.
pub use schnorrkel::vrf::VRF_OUTPUT_LENGTH; | ||
use schnorrkel::{errors::MultiSignatureStage, vrf::VRF_PROOF_LENGTH, SignatureError}; | ||
use schnorrkel::{ | ||
errors::MultiSignatureStage, |
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.
we're going to deprecate the current multi-sig there btw, but maybe that does not help you right now
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 know that schnorrkel 0.11
is coming soon. As soon as it is released there will be a follow up PR with renaming of VrfOutput -> VrfPreOut
, and all not required stuff removal.
This PR is mostly to prepare the soil for a painless introduction of bandersnatch-vrf
bot merge |
Error: "Check reviews" status is not passing for paritytech/polkadot#7063 |
bot merge |
bot merge |
bot merge |
/// VRF Verifier. | ||
pub trait VrfVerifier: VrfCrypto { | ||
/// Verify input data signature. | ||
fn vrf_verify(&self, data: &Self::VrfInput, signature: &Self::VrfSignature) -> bool; |
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 not the general type signature of an EC VRF verify method. An extra message is signed too in general. There may also be multiple input-output pairs. Also these pairs should usually be returned by the verify routine. Also, crypto verifiers should be #[must_use]
which usually suggests they return Result
not bool
.
pub output: VrfOutput, | ||
/// The calculated VRF proof | ||
pub proof: VrfProof, | ||
} |
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.
It is a good idea to consolidate the VrfPreOut
(s) inside the VrfSignature
like this, but realize there might be more than one, as happens in sassafras.
/// VRF Signer. | ||
pub trait VrfSigner: VrfCrypto { | ||
/// Sign input data. | ||
fn vrf_sign(&self, data: &Self::VrfInput) -> Self::VrfSignature; |
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.
It's not possible to know if you should sign or not given this interface. It slows down singing somewhat too.
* First iteration to encapsulate schnorrkel and merlin usage * Remove schnorkel direct dependency from BABE pallet * Remove schnorrkel direct dependency from BABE client * Trivial renaming for VrfTranscript data and value * Better errors * Expose a function to get a schnorrkel friendly transcript * Keep the vrf signature stuff together (preventing some clones around) * Fix tests * Remove vrf agnostic transcript and define it as an associated type for VrfSigner and VrfVerifier * Fix babe pallet mock * Inner types are required to be public for polkadot * Update client/consensus/babe/src/verification.rs Co-authored-by: Koute <koute@users.noreply.github.com> * Nit * Remove Deref implementations * make_bytes as a method * Trigger CI --------- Co-authored-by: Koute <koute@users.noreply.github.com>
For SASSAFRAS we need to introduce a new VRF primitive.
This new primitive:
schnorrkel
merlin
as vrf transcript backend but in the future will not depend on it (cc @burdges).Right now
schnorrkel
andmerlin
dependencies are too much exposed all over the project.We wish to bury them in the
sp-core
crate to allow better coexistence with other VRF implementations and future changes of backends.In this PR:
merlin
andschnorrkel
are confined tosp-core
. Their direct usage has been removed from client, frame and other primitive crates (e.g. keystore).VrfSigner
: for types capable of producing a vrf signature (implemented bysr25519::Pair
)VrfVerifier
: for types capable of verify a vrf signature (implemented bysr25519::Public
)sp-consensus-vrf
crate andsp-keystore::vrf::schnorrkel
module were removed. We rely on vrf types defined bysp-core::crypto
VrfProof
andVrfPreOut
are kept together asVrfSignature
in the BABE digests. This simplifies a bit the code and prevents some copies. Because the objects order is preserved, serialization using SCALE is backward compatibleFurther work:
VrfOutput
in the vrf signature to something likeVrfState
or (as Jeff suggests)VrfPreOut
. Because this is what it is. Technically the VRF output is what is returned by themake_bytes
function called for example to get babe randomness or the slot lottery value.polkadot companion: paritytech/polkadot#7063