mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 22:41:02 +00:00
Make BEEFY payload extensible (#10307)
* make BEEFY payload extensible * cargo fmt * cargo fmt * remove generic payload param in beefy-primitives * cargo fmt * Apply suggestions from code review Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * adds Paylaod Type * remove hex * fix tests * Apply suggestions from code review Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * use binary_search_by to sort * Payload::new() * fix tests * Apply suggestions from code review Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * fix tests * cargo fmt * fix get_decoded * fix test Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Generated
+1
@@ -535,6 +535,7 @@ dependencies = [
|
|||||||
name = "beefy-primitives"
|
name = "beefy-primitives"
|
||||||
version = "4.0.0-dev"
|
version = "4.0.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"scale-info",
|
"scale-info",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use wasm_timer::Instant;
|
|||||||
|
|
||||||
use beefy_primitives::{
|
use beefy_primitives::{
|
||||||
crypto::{Public, Signature},
|
crypto::{Public, Signature},
|
||||||
MmrRootHash, VoteMessage,
|
VoteMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::keystore::BeefyKeystore;
|
use crate::keystore::BeefyKeystore;
|
||||||
@@ -142,9 +142,7 @@ where
|
|||||||
sender: &PeerId,
|
sender: &PeerId,
|
||||||
mut data: &[u8],
|
mut data: &[u8],
|
||||||
) -> ValidationResult<B::Hash> {
|
) -> ValidationResult<B::Hash> {
|
||||||
if let Ok(msg) =
|
if let Ok(msg) = VoteMessage::<NumberFor<B>, Public, Signature>::decode(&mut data) {
|
||||||
VoteMessage::<MmrRootHash, NumberFor<B>, Public, Signature>::decode(&mut data)
|
|
||||||
{
|
|
||||||
let msg_hash = twox_64(data);
|
let msg_hash = twox_64(data);
|
||||||
let round = msg.commitment.block_number;
|
let round = msg.commitment.block_number;
|
||||||
|
|
||||||
@@ -178,9 +176,7 @@ where
|
|||||||
fn message_expired<'a>(&'a self) -> Box<dyn FnMut(B::Hash, &[u8]) -> bool + 'a> {
|
fn message_expired<'a>(&'a self) -> Box<dyn FnMut(B::Hash, &[u8]) -> bool + 'a> {
|
||||||
let known_votes = self.known_votes.read();
|
let known_votes = self.known_votes.read();
|
||||||
Box::new(move |_topic, mut data| {
|
Box::new(move |_topic, mut data| {
|
||||||
let msg = match VoteMessage::<MmrRootHash, NumberFor<B>, Public, Signature>::decode(
|
let msg = match VoteMessage::<NumberFor<B>, Public, Signature>::decode(&mut data) {
|
||||||
&mut data,
|
|
||||||
) {
|
|
||||||
Ok(vote) => vote,
|
Ok(vote) => vote,
|
||||||
Err(_) => return true,
|
Err(_) => return true,
|
||||||
};
|
};
|
||||||
@@ -214,9 +210,7 @@ where
|
|||||||
return do_rebroadcast
|
return do_rebroadcast
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = match VoteMessage::<MmrRootHash, NumberFor<B>, Public, Signature>::decode(
|
let msg = match VoteMessage::<NumberFor<B>, Public, Signature>::decode(&mut data) {
|
||||||
&mut data,
|
|
||||||
) {
|
|
||||||
Ok(vote) => vote,
|
Ok(vote) => vote,
|
||||||
Err(_) => return true,
|
Err(_) => return true,
|
||||||
};
|
};
|
||||||
@@ -237,9 +231,11 @@ mod tests {
|
|||||||
use sc_network_test::Block;
|
use sc_network_test::Block;
|
||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
|
|
||||||
use beefy_primitives::{crypto::Signature, Commitment, MmrRootHash, VoteMessage, KEY_TYPE};
|
|
||||||
|
|
||||||
use crate::keystore::{tests::Keyring, BeefyKeystore};
|
use crate::keystore::{tests::Keyring, BeefyKeystore};
|
||||||
|
use beefy_primitives::{
|
||||||
|
crypto::Signature, known_payload_ids, Commitment, MmrRootHash, Payload, VoteMessage,
|
||||||
|
KEY_TYPE,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -345,10 +341,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_commitment<BN: Encode, P: Encode>(
|
fn sign_commitment<BN: Encode>(who: &Keyring, commitment: &Commitment<BN>) -> Signature {
|
||||||
who: &Keyring,
|
|
||||||
commitment: &Commitment<BN, P>,
|
|
||||||
) -> Signature {
|
|
||||||
let store: SyncCryptoStorePtr = std::sync::Arc::new(LocalKeystore::in_memory());
|
let store: SyncCryptoStorePtr = std::sync::Arc::new(LocalKeystore::in_memory());
|
||||||
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap();
|
SyncCryptoStore::ecdsa_generate_new(&*store, KEY_TYPE, Some(&who.to_seed())).unwrap();
|
||||||
let beefy_keystore: BeefyKeystore = Some(store).into();
|
let beefy_keystore: BeefyKeystore = Some(store).into();
|
||||||
@@ -362,11 +355,8 @@ mod tests {
|
|||||||
let sender = sc_network::PeerId::random();
|
let sender = sc_network::PeerId::random();
|
||||||
let mut context = TestContext;
|
let mut context = TestContext;
|
||||||
|
|
||||||
let commitment = Commitment {
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, MmrRootHash::default().encode());
|
||||||
payload: MmrRootHash::default(),
|
let commitment = Commitment { payload, block_number: 3_u64, validator_set_id: 0 };
|
||||||
block_number: 3_u64,
|
|
||||||
validator_set_id: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let signature = sign_commitment(&Keyring::Alice, &commitment);
|
let signature = sign_commitment(&Keyring::Alice, &commitment);
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ use sp_runtime::traits::{Block, NumberFor};
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
/// Stream of signed commitments returned when subscribing.
|
/// Stream of signed commitments returned when subscribing.
|
||||||
pub type SignedCommitment<Block> =
|
pub type SignedCommitment<Block> = beefy_primitives::SignedCommitment<NumberFor<Block>>;
|
||||||
beefy_primitives::SignedCommitment<NumberFor<Block>, beefy_primitives::MmrRootHash>;
|
|
||||||
|
|
||||||
/// Stream of signed commitments returned when subscribing.
|
/// Stream of signed commitments returned when subscribing.
|
||||||
type SignedCommitmentStream<Block> = TracingUnboundedReceiver<SignedCommitment<Block>>;
|
type SignedCommitmentStream<Block> = TracingUnboundedReceiver<SignedCommitment<Block>>;
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ fn threshold(authorities: usize) -> usize {
|
|||||||
authorities - faulty
|
authorities - faulty
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Rounds<Hash, Number> {
|
pub(crate) struct Rounds<Payload, Number> {
|
||||||
rounds: BTreeMap<(Hash, Number), RoundTracker>,
|
rounds: BTreeMap<(Payload, Number), RoundTracker>,
|
||||||
validator_set: ValidatorSet<Public>,
|
validator_set: ValidatorSet<Public>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, N> Rounds<H, N>
|
impl<P, N> Rounds<P, N>
|
||||||
where
|
where
|
||||||
H: Ord + Hash,
|
P: Ord + Hash,
|
||||||
N: Ord + AtLeast32BitUnsigned + MaybeDisplay,
|
N: Ord + AtLeast32BitUnsigned + MaybeDisplay,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(validator_set: ValidatorSet<Public>) -> Self {
|
pub(crate) fn new(validator_set: ValidatorSet<Public>) -> Self {
|
||||||
@@ -70,8 +70,8 @@ where
|
|||||||
|
|
||||||
impl<H, N> Rounds<H, N>
|
impl<H, N> Rounds<H, N>
|
||||||
where
|
where
|
||||||
H: Ord + Hash,
|
H: Ord + Hash + Clone,
|
||||||
N: Ord + AtLeast32BitUnsigned + MaybeDisplay,
|
N: Ord + AtLeast32BitUnsigned + MaybeDisplay + Clone,
|
||||||
{
|
{
|
||||||
pub(crate) fn validator_set_id(&self) -> ValidatorSetId {
|
pub(crate) fn validator_set_id(&self) -> ValidatorSetId {
|
||||||
self.validator_set.id
|
self.validator_set.id
|
||||||
@@ -81,9 +81,9 @@ where
|
|||||||
self.validator_set.validators.clone()
|
self.validator_set.validators.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_vote(&mut self, round: (H, N), vote: (Public, Signature)) -> bool {
|
pub(crate) fn add_vote(&mut self, round: &(H, N), vote: (Public, Signature)) -> bool {
|
||||||
if self.validator_set.validators.iter().any(|id| vote.0 == *id) {
|
if self.validator_set.validators.iter().any(|id| vote.0 == *id) {
|
||||||
self.rounds.entry(round).or_default().add_vote(vote)
|
self.rounds.entry(round.clone()).or_default().add_vote(vote)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ mod tests {
|
|||||||
let mut rounds = Rounds::<H256, NumberFor<Block>>::new(validators);
|
let mut rounds = Rounds::<H256, NumberFor<Block>>::new(validators);
|
||||||
|
|
||||||
assert!(rounds.add_vote(
|
assert!(rounds.add_vote(
|
||||||
(H256::from_low_u64_le(1), 1),
|
&(H256::from_low_u64_le(1), 1),
|
||||||
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed"))
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed"))
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -187,21 +187,21 @@ mod tests {
|
|||||||
|
|
||||||
// invalid vote
|
// invalid vote
|
||||||
assert!(!rounds.add_vote(
|
assert!(!rounds.add_vote(
|
||||||
(H256::from_low_u64_le(1), 1),
|
&(H256::from_low_u64_le(1), 1),
|
||||||
(Keyring::Dave.public(), Keyring::Dave.sign(b"I am committed"))
|
(Keyring::Dave.public(), Keyring::Dave.sign(b"I am committed"))
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
||||||
|
|
||||||
assert!(rounds.add_vote(
|
assert!(rounds.add_vote(
|
||||||
(H256::from_low_u64_le(1), 1),
|
&(H256::from_low_u64_le(1), 1),
|
||||||
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed"))
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed"))
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
assert!(!rounds.is_done(&(H256::from_low_u64_le(1), 1)));
|
||||||
|
|
||||||
assert!(rounds.add_vote(
|
assert!(rounds.add_vote(
|
||||||
(H256::from_low_u64_le(1), 1),
|
&(H256::from_low_u64_le(1), 1),
|
||||||
(Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am committed"))
|
(Keyring::Charlie.public(), Keyring::Charlie.sign(b"I am committed"))
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -225,31 +225,31 @@ mod tests {
|
|||||||
|
|
||||||
// round 1
|
// round 1
|
||||||
rounds.add_vote(
|
rounds.add_vote(
|
||||||
(H256::from_low_u64_le(1), 1),
|
&(H256::from_low_u64_le(1), 1),
|
||||||
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")),
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed")),
|
||||||
);
|
);
|
||||||
rounds.add_vote(
|
rounds.add_vote(
|
||||||
(H256::from_low_u64_le(1), 1),
|
&(H256::from_low_u64_le(1), 1),
|
||||||
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")),
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed")),
|
||||||
);
|
);
|
||||||
|
|
||||||
// round 2
|
// round 2
|
||||||
rounds.add_vote(
|
rounds.add_vote(
|
||||||
(H256::from_low_u64_le(2), 2),
|
&(H256::from_low_u64_le(2), 2),
|
||||||
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am again committed")),
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am again committed")),
|
||||||
);
|
);
|
||||||
rounds.add_vote(
|
rounds.add_vote(
|
||||||
(H256::from_low_u64_le(2), 2),
|
&(H256::from_low_u64_le(2), 2),
|
||||||
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am again committed")),
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am again committed")),
|
||||||
);
|
);
|
||||||
|
|
||||||
// round 3
|
// round 3
|
||||||
rounds.add_vote(
|
rounds.add_vote(
|
||||||
(H256::from_low_u64_le(3), 3),
|
&(H256::from_low_u64_le(3), 3),
|
||||||
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am still committed")),
|
(Keyring::Alice.public(), Keyring::Alice.sign(b"I am still committed")),
|
||||||
);
|
);
|
||||||
rounds.add_vote(
|
rounds.add_vote(
|
||||||
(H256::from_low_u64_le(3), 3),
|
&(H256::from_low_u64_le(3), 3),
|
||||||
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am still committed")),
|
(Keyring::Bob.public(), Keyring::Bob.sign(b"I am still committed")),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ use sp_runtime::{
|
|||||||
|
|
||||||
use beefy_primitives::{
|
use beefy_primitives::{
|
||||||
crypto::{AuthorityId, Public, Signature},
|
crypto::{AuthorityId, Public, Signature},
|
||||||
BeefyApi, Commitment, ConsensusLog, MmrRootHash, SignedCommitment, ValidatorSet,
|
known_payload_ids, BeefyApi, Commitment, ConsensusLog, MmrRootHash, Payload, SignedCommitment,
|
||||||
VersionedCommitment, VoteMessage, BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID,
|
ValidatorSet, VersionedCommitment, VoteMessage, BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -79,7 +79,7 @@ where
|
|||||||
/// Min delta in block numbers between two blocks, BEEFY should vote on
|
/// Min delta in block numbers between two blocks, BEEFY should vote on
|
||||||
min_block_delta: u32,
|
min_block_delta: u32,
|
||||||
metrics: Option<Metrics>,
|
metrics: Option<Metrics>,
|
||||||
rounds: round::Rounds<MmrRootHash, NumberFor<B>>,
|
rounds: round::Rounds<Payload, NumberFor<B>>,
|
||||||
finality_notifications: FinalityNotifications<B>,
|
finality_notifications: FinalityNotifications<B>,
|
||||||
/// Best block we received a GRANDPA notification for
|
/// Best block we received a GRANDPA notification for
|
||||||
best_grandpa_block: NumberFor<B>,
|
best_grandpa_block: NumberFor<B>,
|
||||||
@@ -262,8 +262,9 @@ where
|
|||||||
return
|
return
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, mmr_root.encode());
|
||||||
let commitment = Commitment {
|
let commitment = Commitment {
|
||||||
payload: mmr_root,
|
payload,
|
||||||
block_number: notification.header.number(),
|
block_number: notification.header.number(),
|
||||||
validator_set_id: self.rounds.validator_set_id(),
|
validator_set_id: self.rounds.validator_set_id(),
|
||||||
};
|
};
|
||||||
@@ -301,10 +302,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_vote(&mut self, round: (MmrRootHash, NumberFor<B>), vote: (Public, Signature)) {
|
fn handle_vote(&mut self, round: (Payload, NumberFor<B>), vote: (Public, Signature)) {
|
||||||
self.gossip_validator.note_round(round.1);
|
self.gossip_validator.note_round(round.1);
|
||||||
|
|
||||||
let vote_added = self.rounds.add_vote(round, vote);
|
let vote_added = self.rounds.add_vote(&round, vote);
|
||||||
|
|
||||||
if vote_added && self.rounds.is_done(&round) {
|
if vote_added && self.rounds.is_done(&round) {
|
||||||
if let Some(signatures) = self.rounds.drop(&round) {
|
if let Some(signatures) = self.rounds.drop(&round) {
|
||||||
@@ -352,7 +353,7 @@ where
|
|||||||
|notification| async move {
|
|notification| async move {
|
||||||
debug!(target: "beefy", "🥩 Got vote message: {:?}", notification);
|
debug!(target: "beefy", "🥩 Got vote message: {:?}", notification);
|
||||||
|
|
||||||
VoteMessage::<MmrRootHash, NumberFor<B>, Public, Signature>::decode(
|
VoteMessage::<NumberFor<B>, Public, Signature>::decode(
|
||||||
&mut ¬ification.message[..],
|
&mut ¬ification.message[..],
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ sp-runtime = { version = "4.0.0-dev", path = "../runtime", default-features = fa
|
|||||||
sp-std = { version = "4.0.0-dev", path = "../std", default-features = false }
|
sp-std = { version = "4.0.0-dev", path = "../std", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
hex = "0.4.3"
|
||||||
hex-literal = "0.3"
|
hex-literal = "0.3"
|
||||||
|
|
||||||
sp-keystore = { version = "0.10.0-dev", path = "../keystore" }
|
sp-keystore = { version = "0.10.0-dev", path = "../keystore" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -15,10 +15,65 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use codec::{Decode, Encode};
|
||||||
use sp_std::{cmp, prelude::*};
|
use sp_std::{cmp, prelude::*};
|
||||||
|
|
||||||
use crate::{crypto::Signature, ValidatorSetId};
|
use crate::{crypto::Signature, ValidatorSetId};
|
||||||
|
|
||||||
|
/// Id of different payloads in the [`Commitment`] data
|
||||||
|
pub type BeefyPayloadId = [u8; 2];
|
||||||
|
|
||||||
|
/// Registry of all known [`BeefyPayloadId`].
|
||||||
|
pub mod known_payload_ids {
|
||||||
|
use crate::BeefyPayloadId;
|
||||||
|
|
||||||
|
/// A [`Payload`] identifier for Merkle Mountain Range root hash.
|
||||||
|
///
|
||||||
|
/// Encoded value should contain a [`beefy_primitives::MmrRootHash`] type (i.e. 32-bytes hash).
|
||||||
|
pub const MMR_ROOT_ID: BeefyPayloadId = *b"mh";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A BEEFY payload type allowing for future extensibility of adding additional kinds of payloads.
|
||||||
|
///
|
||||||
|
/// The idea is to store a vector of SCALE-encoded values with an extra identifier.
|
||||||
|
/// Identifiers MUST be sorted by the [`BeefyPayloadId`] to allow efficient lookup of expected
|
||||||
|
/// value. Duplicated identifiers are disallowed. It's okay for different implementations to only
|
||||||
|
/// support a subset of possible values.
|
||||||
|
#[derive(Decode, Encode, Debug, PartialEq, Eq, Clone, Ord, PartialOrd, Hash)]
|
||||||
|
pub struct Payload(Vec<(BeefyPayloadId, Vec<u8>)>);
|
||||||
|
|
||||||
|
impl Payload {
|
||||||
|
/// Construct a new payload given an initial vallue
|
||||||
|
pub fn new(id: BeefyPayloadId, value: Vec<u8>) -> Self {
|
||||||
|
Self(vec![(id, value)])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a raw payload under given `id`.
|
||||||
|
///
|
||||||
|
/// If the [`BeefyPayloadId`] is not found in the payload `None` is returned.
|
||||||
|
pub fn get_raw(&self, id: &BeefyPayloadId) -> Option<&Vec<u8>> {
|
||||||
|
let index = self.0.binary_search_by(|probe| probe.0.cmp(id)).ok()?;
|
||||||
|
Some(&self.0[index].1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a decoded payload value under given `id`.
|
||||||
|
///
|
||||||
|
/// In case the value is not there or it cannot be decoded does not match `None` is returned.
|
||||||
|
pub fn get_decoded<T: Decode>(&self, id: &BeefyPayloadId) -> Option<T> {
|
||||||
|
self.get_raw(id).and_then(|raw| T::decode(&mut &raw[..]).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a `Vec<u8>` with a given id into the payload vec.
|
||||||
|
/// This method will internally sort the payload vec after every push.
|
||||||
|
///
|
||||||
|
/// Returns self to allow for daisy chaining.
|
||||||
|
pub fn push_raw(mut self, id: BeefyPayloadId, value: Vec<u8>) -> Self {
|
||||||
|
self.0.push((id, value));
|
||||||
|
self.0.sort_by_key(|(id, _)| *id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A commitment signed by GRANDPA validators as part of BEEFY protocol.
|
/// A commitment signed by GRANDPA validators as part of BEEFY protocol.
|
||||||
///
|
///
|
||||||
/// The commitment contains a [payload](Commitment::payload) extracted from the finalized block at
|
/// The commitment contains a [payload](Commitment::payload) extracted from the finalized block at
|
||||||
@@ -26,16 +81,17 @@ use crate::{crypto::Signature, ValidatorSetId};
|
|||||||
/// GRANDPA validators collect signatures on commitments and a stream of such signed commitments
|
/// GRANDPA validators collect signatures on commitments and a stream of such signed commitments
|
||||||
/// (see [SignedCommitment]) forms the BEEFY protocol.
|
/// (see [SignedCommitment]) forms the BEEFY protocol.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
|
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
|
||||||
pub struct Commitment<TBlockNumber, TPayload> {
|
pub struct Commitment<TBlockNumber> {
|
||||||
/// The payload being signed.
|
/// A collection of payloads to be signed, see [`Payload`] for details.
|
||||||
///
|
///
|
||||||
/// This should be some form of cumulative representation of the chain (think MMR root hash).
|
/// One of the payloads should be some form of cumulative representation of the chain (think
|
||||||
/// The payload should also contain some details that allow the light client to verify next
|
/// MMR root hash). Additionally one of the payloads should also contain some details that
|
||||||
/// validator set. The protocol does not enforce any particular format of this data,
|
/// allow the light client to verify next validator set. The protocol does not enforce any
|
||||||
/// nor how often it should be present in commitments, however the light client has to be
|
/// particular format of this data, nor how often it should be present in commitments, however
|
||||||
/// provided with full validator set whenever it performs the transition (i.e. importing first
|
/// the light client has to be provided with full validator set whenever it performs the
|
||||||
/// block with [validator_set_id](Commitment::validator_set_id) incremented).
|
/// transition (i.e. importing first block with
|
||||||
pub payload: TPayload,
|
/// [validator_set_id](Commitment::validator_set_id) incremented).
|
||||||
|
pub payload: Payload,
|
||||||
|
|
||||||
/// Finalized block number this commitment is for.
|
/// Finalized block number this commitment is for.
|
||||||
///
|
///
|
||||||
@@ -57,20 +113,18 @@ pub struct Commitment<TBlockNumber, TPayload> {
|
|||||||
pub validator_set_id: ValidatorSetId,
|
pub validator_set_id: ValidatorSetId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TBlockNumber, TPayload> cmp::PartialOrd for Commitment<TBlockNumber, TPayload>
|
impl<TBlockNumber> cmp::PartialOrd for Commitment<TBlockNumber>
|
||||||
where
|
where
|
||||||
TBlockNumber: cmp::Ord,
|
TBlockNumber: cmp::Ord,
|
||||||
TPayload: cmp::Eq,
|
|
||||||
{
|
{
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TBlockNumber, TPayload> cmp::Ord for Commitment<TBlockNumber, TPayload>
|
impl<TBlockNumber> cmp::Ord for Commitment<TBlockNumber>
|
||||||
where
|
where
|
||||||
TBlockNumber: cmp::Ord,
|
TBlockNumber: cmp::Ord,
|
||||||
TPayload: cmp::Eq,
|
|
||||||
{
|
{
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
self.validator_set_id
|
self.validator_set_id
|
||||||
@@ -81,9 +135,9 @@ where
|
|||||||
|
|
||||||
/// A commitment with matching GRANDPA validators' signatures.
|
/// A commitment with matching GRANDPA validators' signatures.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
|
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
|
||||||
pub struct SignedCommitment<TBlockNumber, TPayload> {
|
pub struct SignedCommitment<TBlockNumber> {
|
||||||
/// The commitment signatures are collected for.
|
/// The commitment signatures are collected for.
|
||||||
pub commitment: Commitment<TBlockNumber, TPayload>,
|
pub commitment: Commitment<TBlockNumber>,
|
||||||
/// GRANDPA validators' signatures for the commitment.
|
/// GRANDPA validators' signatures for the commitment.
|
||||||
///
|
///
|
||||||
/// The length of this `Vec` must match number of validators in the current set (see
|
/// The length of this `Vec` must match number of validators in the current set (see
|
||||||
@@ -91,7 +145,7 @@ pub struct SignedCommitment<TBlockNumber, TPayload> {
|
|||||||
pub signatures: Vec<Option<Signature>>,
|
pub signatures: Vec<Option<Signature>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TBlockNumber, TPayload> SignedCommitment<TBlockNumber, TPayload> {
|
impl<TBlockNumber> SignedCommitment<TBlockNumber> {
|
||||||
/// Return the number of collected signatures.
|
/// Return the number of collected signatures.
|
||||||
pub fn no_of_signatures(&self) -> usize {
|
pub fn no_of_signatures(&self) -> usize {
|
||||||
self.signatures.iter().filter(|x| x.is_some()).count()
|
self.signatures.iter().filter(|x| x.is_some()).count()
|
||||||
@@ -102,10 +156,10 @@ impl<TBlockNumber, TPayload> SignedCommitment<TBlockNumber, TPayload> {
|
|||||||
/// to the block justifications for the block for which the signed commitment
|
/// to the block justifications for the block for which the signed commitment
|
||||||
/// has been generated.
|
/// has been generated.
|
||||||
#[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
|
#[derive(Clone, Debug, PartialEq, codec::Encode, codec::Decode)]
|
||||||
pub enum VersionedCommitment<N, P> {
|
pub enum VersionedCommitment<N> {
|
||||||
#[codec(index = 1)]
|
#[codec(index = 1)]
|
||||||
/// Current active version
|
/// Current active version
|
||||||
V1(SignedCommitment<N, P>),
|
V1(SignedCommitment<N>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -119,9 +173,9 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{crypto, KEY_TYPE};
|
use crate::{crypto, KEY_TYPE};
|
||||||
|
|
||||||
type TestCommitment = Commitment<u128, String>;
|
type TestCommitment = Commitment<u128>;
|
||||||
type TestSignedCommitment = SignedCommitment<u128, String>;
|
type TestSignedCommitment = SignedCommitment<u128>;
|
||||||
type TestVersionedCommitment = VersionedCommitment<u128, String>;
|
type TestVersionedCommitment = VersionedCommitment<u128>;
|
||||||
|
|
||||||
// The mock signatures are equivalent to the ones produced by the BEEFY keystore
|
// The mock signatures are equivalent to the ones produced by the BEEFY keystore
|
||||||
fn mock_signatures() -> (crypto::Signature, crypto::Signature) {
|
fn mock_signatures() -> (crypto::Signature, crypto::Signature) {
|
||||||
@@ -148,8 +202,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn commitment_encode_decode() {
|
fn commitment_encode_decode() {
|
||||||
// given
|
// given
|
||||||
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".encode());
|
||||||
let commitment: TestCommitment =
|
let commitment: TestCommitment =
|
||||||
Commitment { payload: "Hello World!".into(), block_number: 5, validator_set_id: 0 };
|
Commitment { payload, block_number: 5, validator_set_id: 0 };
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let encoded = codec::Encode::encode(&commitment);
|
let encoded = codec::Encode::encode(&commitment);
|
||||||
@@ -160,7 +215,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
encoded,
|
encoded,
|
||||||
hex_literal::hex!(
|
hex_literal::hex!(
|
||||||
"3048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000"
|
"046d68343048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -168,8 +223,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn signed_commitment_encode_decode() {
|
fn signed_commitment_encode_decode() {
|
||||||
// given
|
// given
|
||||||
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".encode());
|
||||||
let commitment: TestCommitment =
|
let commitment: TestCommitment =
|
||||||
Commitment { payload: "Hello World!".into(), block_number: 5, validator_set_id: 0 };
|
Commitment { payload, block_number: 5, validator_set_id: 0 };
|
||||||
|
|
||||||
let sigs = mock_signatures();
|
let sigs = mock_signatures();
|
||||||
|
|
||||||
@@ -187,10 +243,11 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
encoded,
|
encoded,
|
||||||
hex_literal::hex!(
|
hex_literal::hex!(
|
||||||
"3048656c6c6f20576f726c64210500000000000000000000000000000000000000000000001000
|
"046d68343048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000
|
||||||
0001558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d
|
10000001558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321
|
||||||
10dd3cd68ce3dc0c33c86e99bcb7816f9ba01012d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a
|
f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba01012d6e1f8105c337a86cdd9aaa
|
||||||
2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a00"
|
cdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a
|
||||||
|
0046395a71681be3d0c2a00"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -198,8 +255,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn signed_commitment_count_signatures() {
|
fn signed_commitment_count_signatures() {
|
||||||
// given
|
// given
|
||||||
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".encode());
|
||||||
let commitment: TestCommitment =
|
let commitment: TestCommitment =
|
||||||
Commitment { payload: "Hello World!".into(), block_number: 5, validator_set_id: 0 };
|
Commitment { payload, block_number: 5, validator_set_id: 0 };
|
||||||
|
|
||||||
let sigs = mock_signatures();
|
let sigs = mock_signatures();
|
||||||
|
|
||||||
@@ -222,7 +280,8 @@ mod tests {
|
|||||||
block_number: u128,
|
block_number: u128,
|
||||||
validator_set_id: crate::ValidatorSetId,
|
validator_set_id: crate::ValidatorSetId,
|
||||||
) -> TestCommitment {
|
) -> TestCommitment {
|
||||||
Commitment { payload: "Hello World!".into(), block_number, validator_set_id }
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".encode());
|
||||||
|
Commitment { payload, block_number, validator_set_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
// given
|
// given
|
||||||
@@ -241,8 +300,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn versioned_commitment_encode_decode() {
|
fn versioned_commitment_encode_decode() {
|
||||||
|
let payload = Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".encode());
|
||||||
let commitment: TestCommitment =
|
let commitment: TestCommitment =
|
||||||
Commitment { payload: "Hello World!".into(), block_number: 5, validator_set_id: 0 };
|
Commitment { payload, block_number: 5, validator_set_id: 0 };
|
||||||
|
|
||||||
let sigs = mock_signatures();
|
let sigs = mock_signatures();
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ mod commitment;
|
|||||||
pub mod mmr;
|
pub mod mmr;
|
||||||
pub mod witness;
|
pub mod witness;
|
||||||
|
|
||||||
pub use commitment::{Commitment, SignedCommitment, VersionedCommitment};
|
pub use commitment::{
|
||||||
|
known_payload_ids, BeefyPayloadId, Commitment, Payload, SignedCommitment, VersionedCommitment,
|
||||||
|
};
|
||||||
|
|
||||||
use codec::{Codec, Decode, Encode};
|
use codec::{Codec, Decode, Encode};
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
@@ -118,9 +120,9 @@ pub enum ConsensusLog<AuthorityId: Codec> {
|
|||||||
/// A vote message is a direct vote created by a BEEFY node on every voting round
|
/// A vote message is a direct vote created by a BEEFY node on every voting round
|
||||||
/// and is gossiped to its peers.
|
/// and is gossiped to its peers.
|
||||||
#[derive(Debug, Decode, Encode, TypeInfo)]
|
#[derive(Debug, Decode, Encode, TypeInfo)]
|
||||||
pub struct VoteMessage<Hash, Number, Id, Signature> {
|
pub struct VoteMessage<Number, Id, Signature> {
|
||||||
/// Commit to information extracted from a finalized block
|
/// Commit to information extracted from a finalized block
|
||||||
pub commitment: Commitment<Number, Hash>,
|
pub commitment: Commitment<Number>,
|
||||||
/// Node authority id
|
/// Node authority id
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
/// Node signature
|
/// Node signature
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ use crate::{
|
|||||||
/// Ethereum Mainnet), in a commit-reveal like scheme, where first we submit only the signed
|
/// Ethereum Mainnet), in a commit-reveal like scheme, where first we submit only the signed
|
||||||
/// commitment witness and later on, the client picks only some signatures to verify at random.
|
/// commitment witness and later on, the client picks only some signatures to verify at random.
|
||||||
#[derive(Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
|
#[derive(Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
|
||||||
pub struct SignedCommitmentWitness<TBlockNumber, TPayload, TMerkleRoot> {
|
pub struct SignedCommitmentWitness<TBlockNumber, TMerkleRoot> {
|
||||||
/// The full content of the commitment.
|
/// The full content of the commitment.
|
||||||
pub commitment: Commitment<TBlockNumber, TPayload>,
|
pub commitment: Commitment<TBlockNumber>,
|
||||||
|
|
||||||
/// The bit vector of validators who signed the commitment.
|
/// The bit vector of validators who signed the commitment.
|
||||||
pub signed_by: Vec<bool>, // TODO [ToDr] Consider replacing with bitvec crate
|
pub signed_by: Vec<bool>, // TODO [ToDr] Consider replacing with bitvec crate
|
||||||
@@ -51,9 +51,7 @@ pub struct SignedCommitmentWitness<TBlockNumber, TPayload, TMerkleRoot> {
|
|||||||
pub signatures_merkle_root: TMerkleRoot,
|
pub signatures_merkle_root: TMerkleRoot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TBlockNumber, TPayload, TMerkleRoot>
|
impl<TBlockNumber, TMerkleRoot> SignedCommitmentWitness<TBlockNumber, TMerkleRoot> {
|
||||||
SignedCommitmentWitness<TBlockNumber, TPayload, TMerkleRoot>
|
|
||||||
{
|
|
||||||
/// Convert [SignedCommitment] into [SignedCommitmentWitness].
|
/// Convert [SignedCommitment] into [SignedCommitmentWitness].
|
||||||
///
|
///
|
||||||
/// This takes a [SignedCommitment], which contains full signatures
|
/// This takes a [SignedCommitment], which contains full signatures
|
||||||
@@ -63,7 +61,7 @@ impl<TBlockNumber, TPayload, TMerkleRoot>
|
|||||||
///
|
///
|
||||||
/// Returns the full list of signatures along with the witness.
|
/// Returns the full list of signatures along with the witness.
|
||||||
pub fn from_signed<TMerkelize>(
|
pub fn from_signed<TMerkelize>(
|
||||||
signed: SignedCommitment<TBlockNumber, TPayload>,
|
signed: SignedCommitment<TBlockNumber>,
|
||||||
merkelize: TMerkelize,
|
merkelize: TMerkelize,
|
||||||
) -> (Self, Vec<Option<Signature>>)
|
) -> (Self, Vec<Option<Signature>>)
|
||||||
where
|
where
|
||||||
@@ -86,12 +84,11 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
|
|
||||||
use crate::{crypto, KEY_TYPE};
|
use crate::{crypto, known_payload_ids, Payload, KEY_TYPE};
|
||||||
|
|
||||||
type TestCommitment = Commitment<u128, String>;
|
type TestCommitment = Commitment<u128>;
|
||||||
type TestSignedCommitment = SignedCommitment<u128, String>;
|
type TestSignedCommitment = SignedCommitment<u128>;
|
||||||
type TestSignedCommitmentWitness =
|
type TestSignedCommitmentWitness = SignedCommitmentWitness<u128, Vec<Option<Signature>>>;
|
||||||
SignedCommitmentWitness<u128, String, Vec<Option<Signature>>>;
|
|
||||||
|
|
||||||
// The mock signatures are equivalent to the ones produced by the BEEFY keystore
|
// The mock signatures are equivalent to the ones produced by the BEEFY keystore
|
||||||
fn mock_signatures() -> (crypto::Signature, crypto::Signature) {
|
fn mock_signatures() -> (crypto::Signature, crypto::Signature) {
|
||||||
@@ -116,8 +113,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signed_commitment() -> TestSignedCommitment {
|
fn signed_commitment() -> TestSignedCommitment {
|
||||||
|
let payload =
|
||||||
|
Payload::new(known_payload_ids::MMR_ROOT_ID, "Hello World!".as_bytes().to_vec());
|
||||||
let commitment: TestCommitment =
|
let commitment: TestCommitment =
|
||||||
Commitment { payload: "Hello World!".into(), block_number: 5, validator_set_id: 0 };
|
Commitment { payload, block_number: 5, validator_set_id: 0 };
|
||||||
|
|
||||||
let sigs = mock_signatures();
|
let sigs = mock_signatures();
|
||||||
|
|
||||||
@@ -152,10 +151,11 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
encoded,
|
encoded,
|
||||||
hex_literal::hex!(
|
hex_literal::hex!(
|
||||||
"3048656c6c6f20576f726c64210500000000000000000000000000000000000000000000001000
|
"046d683048656c6c6f20576f726c642105000000000000000000000000000000000000000000000010
|
||||||
00010110000001558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e9
|
0000010110000001558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c
|
||||||
9a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba01012d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd
|
746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba01012d6e1f8105c337a86
|
||||||
48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a00"
|
cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bc
|
||||||
|
a2324b6a0046395a71681be3d0c2a00"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user