Use proper messages for ValidatorSets/InInstructions pallet

Provides a DST, and associated metadata as beneficial.

Also utilizes MuSig's context to session-bind. Since set_keys_messages also
binds to set, this is semi-redundant, yet that's appreciated.
This commit is contained in:
Luke Parker
2023-05-13 04:20:13 -04:00
parent 663b5f4b50
commit 47f8766da6
17 changed files with 102 additions and 70 deletions

View File

@@ -14,9 +14,6 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
hashbrown = { version = "0.13", default-features = false }
ciphersuite = { version = "0.3", path = "../../../crypto/ciphersuite", default-features = false, features = ["ristretto"] }
dkg = { version = "0.4", path = "../../../crypto/dkg", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2", default-features = false, features = ["derive"] }
@@ -31,12 +28,12 @@ validator-sets-primitives = { path = "../primitives", default-features = false }
[features]
std = [
"ciphersuite/std",
"dkg/std",
"scale/std",
"scale-info/std",
"sp-core/std",
"sp-application-crypto/std",
"frame-system/std",
"frame-support/std",

View File

@@ -69,23 +69,14 @@ pub mod pallet {
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
let hash_set = self.participants.iter().map(|key| key.0).collect::<hashbrown::HashSet<[u8; 32]>>();
if hash_set.len() != self.participants.len()
{
let hash_set =
self.participants.iter().map(|key| key.0).collect::<hashbrown::HashSet<[u8; 32]>>();
if hash_set.len() != self.participants.len() {
panic!("participants contained duplicates");
}
let mut participants = Vec::new();
let mut keys = Vec::new();
for participant in self.participants.clone() {
keys.push(
<Ristretto as Ciphersuite>::read_G::<&[u8]>(
&mut participant.0.as_ref(),
)
.expect("invalid participant"),
);
participants.push((participant, self.bond));
}
let participants = BoundedVec::try_from(participants).unwrap();
@@ -99,7 +90,7 @@ pub mod pallet {
Some(ValidatorSetData { bond: self.bond, network, participants: participants.clone() }),
);
MuSigKeys::<T>::set(set, Some(Public(dkg::musig::musig_key::<Ristretto>(&keys).unwrap().to_bytes())));
MuSigKeys::<T>::set(set, Some(musig_key(set, &self.participants)));
Pallet::<T>::deposit_event(Event::NewSet { set })
}
}
@@ -128,7 +119,7 @@ pub mod pallet {
let Some(musig_key) = MuSigKeys::<T>::get(set) else {
Err(Error::NonExistentValidatorSet)?
};
if !musig_key.verify(&key_pair.encode(), signature) {
if !musig_key.verify(&set_keys_message(&set, key_pair), signature) {
Err(Error::BadSignature)?;
}
@@ -179,7 +170,9 @@ pub mod pallet {
let set = ValidatorSet { session, network: *network };
match Self::verify_signature(set, key_pair, signature) {
Err(Error::AlreadyGeneratedKeys) => Err(InvalidTransaction::Stale)?,
Err(Error::NonExistentValidatorSet) | Err(Error::BadSignature) => Err(InvalidTransaction::BadProof)?,
Err(Error::NonExistentValidatorSet) | Err(Error::BadSignature) => {
Err(InvalidTransaction::BadProof)?
}
Err(Error::__Ignore(_, _)) => unreachable!(),
Ok(()) => (),
}

View File

@@ -14,15 +14,19 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
zeroize = { version = "^1.5", features = ["derive"], optional = true }
serde = { version = "1", features = ["derive"], optional = true }
ciphersuite = { path = "../../../crypto/ciphersuite", version = "0.3", default-features = false, features = ["alloc", "ristretto"] }
dkg = { path = "../../../crypto/dkg", version = "0.4", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
scale-info = { version = "2", default-features = false, features = ["derive"] }
serde = { version = "1", features = ["derive"], optional = true }
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
serai-primitives = { path = "../..//primitives", default-features = false }
serai-primitives = { path = "../../primitives", default-features = false }
[features]
std = ["zeroize", "scale/std", "scale-info/std", "serde", "sp-core/std", "serai-primitives/std"]
std = ["zeroize", "serde", "ciphersuite/std", "dkg/std", "scale/std", "scale-info/std", "sp-core/std", "sp-std/std", "serai-primitives/std"]
default = ["std"]

View File

@@ -3,13 +3,17 @@
#[cfg(feature = "std")]
use zeroize::Zeroize;
use scale::{Encode, Decode, MaxEncodedLen};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use sp_core::{ConstU32, sr25519, bounded::BoundedVec};
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
use scale::{Encode, Decode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{ConstU32, sr25519::Public, bounded::BoundedVec};
#[cfg(not(feature = "std"))]
use sp_std::vec::Vec;
use serai_primitives::{NetworkId, Network, Amount};
@@ -37,13 +41,38 @@ pub struct ValidatorSetData {
// Participant and their amount bonded to this set
// Limit each set to 100 participants for now
pub participants: BoundedVec<(sr25519::Public, Amount), ConstU32<100>>,
pub participants: BoundedVec<(Public, Amount), ConstU32<100>>,
}
type MaxKeyLen = ConstU32<MAX_KEY_LEN>;
/// The type representing a Key from an external network.
pub type ExternalKey = BoundedVec<u8, MaxKeyLen>;
/// A Validator Set's Ristretto key, used for signing InInstructions, and their key on the external
/// network.
pub type KeyPair = (sr25519::Public, ExternalKey);
/// The key pair for a validator set.
///
/// This is their Ristretto key, used for signing Batches, and their key on the external network.
pub type KeyPair = (Public, ExternalKey);
/// The MuSig context for a validator set.
pub fn musig_context(set: ValidatorSet) -> Vec<u8> {
[b"ValidatorSets-musig_key".as_ref(), &set.encode()].concat()
}
/// The MuSig public key for a validator set.
///
/// This function panics on invalid input.
pub fn musig_key(set: ValidatorSet, set_keys: &[Public]) -> Public {
let mut keys = Vec::new();
for key in set_keys {
keys.push(
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut key.0.as_ref())
.expect("invalid participant"),
);
}
Public(dkg::musig::musig_key::<Ristretto>(&musig_context(set), &keys).unwrap().to_bytes())
}
/// The message for the set_keys signature.
pub fn set_keys_message(set: &ValidatorSet, key_pair: &KeyPair) -> Vec<u8> {
[b"ValidatorSets-key_pair".as_ref(), &(set, key_pair).encode()].concat()
}