Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit ad86b0e

Browse files
adoerrtomusdrw
andauthored
Beefy node cleanup (#75)
* bump serde * bump substrate, scale-codec 2.0.0 * we need a proper beefy node * rename primitives as well * Sort members. Co-authored-by: Tomasz Drwięga <tomasz@parity.io>
1 parent 85d81b2 commit ad86b0e

File tree

13 files changed

+1309
-19
lines changed

13 files changed

+1309
-19
lines changed

client/beefy/Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ edition = "2018"
66
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
77

88
[dependencies]
9-
codec = { version = "1.3.5", package = "parity-scale-codec", features = ["derive"] }
109
futures = "0.3"
1110
log = "0.4"
1211
parking_lot = "0.11"
1312

14-
beefy-primitives = { path = "../primitives" }
13+
codec = { version = "2.0.0", package = "parity-scale-codec", features = ["derive"] }
1514

1615
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
1716
sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -26,3 +25,5 @@ sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "mas
2625
sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
2726
sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" }
2827
sc-network-gossip = { git = "https://github.com/paritytech/substrate", branch = "master" }
28+
29+
beefy-primitives = { path = "../beefy-primitives" }

client/beefy/rpc/Cargo.toml

+14-10
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@ edition = "2018"
66
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
77

88
[dependencies]
9-
beefy-gadget = { path = "../." }
10-
beefy-primitives = { path = "../../primitives" }
9+
futures = { version = "0.3.12", features = ["compat"] }
10+
log = "0.4"
11+
serde = { version = "1.0.123", features = ["derive"] }
12+
serde_json = "1.0.61"
1113

12-
codec = { version = "1.3.5", package = "parity-scale-codec", features = ["derive"] }
13-
sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
14-
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
15-
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
1614
jsonrpc-core = "15.1.0"
1715
jsonrpc-core-client = "15.1.0"
1816
jsonrpc-derive = "15.1.0"
1917
jsonrpc-pubsub = "15.1.0"
20-
futures = { version = "0.3.12", features = ["compat"] }
21-
serde = { version = "1.0.119", features = ["derive"] }
22-
serde_json = "1.0.61"
23-
log = "0.4.13"
18+
19+
codec = { version = "2.0.0", package = "parity-scale-codec", features = ["derive"] }
20+
21+
sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" }
22+
23+
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
24+
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
25+
26+
beefy-gadget = { path = "../." }
27+
beefy-primitives = { path = "../../beefy-primitives" }

frame/beefy/Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ edition = "2018"
66
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
77

88
[dependencies]
9-
codec = { version = "1.3.5", package = "parity-scale-codec", default-features = false, features = ["derive"] }
10-
serde = { version = "1.0.119", optional = true }
11-
12-
beefy-primitives = { path = "../primitives", default-features = false }
9+
codec = { version = "2.0.0", package = "parity-scale-codec", default-features = false, features = ["derive"] }
10+
serde = { version = "1.0.123", optional = true }
1311

1412
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
1513
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
@@ -19,6 +17,8 @@ sp-std = { git = "https://github.com/paritytech/substrate", default-features = f
1917

2018
pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
2119

20+
beefy-primitives = { path = "../beefy-primitives", default-features = false }
21+
2222
[dev-dependencies]
2323
sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
2424
sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "master" }

frame/beefy/src/lib.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@
1818

1919
use codec::Encode;
2020

21-
use beefy_primitives::{AuthorityIndex, ConsensusLog, BEEFY_ENGINE_ID};
22-
use frame_support::{decl_module, decl_storage, Parameter};
21+
use frame_support::{decl_module, decl_storage, traits::OneSessionHandler, Parameter};
22+
2323
use sp_runtime::{
2424
generic::DigestItem,
2525
traits::{IsMember, Member},
2626
RuntimeAppPublic,
2727
};
2828
use sp_std::prelude::*;
2929

30+
use beefy_primitives::{AuthorityIndex, ConsensusLog, BEEFY_ENGINE_ID};
31+
3032
#[cfg(test)]
3133
mod mock;
3234

@@ -91,7 +93,7 @@ impl<T: Config> sp_runtime::BoundToRuntimeAppPublic for Module<T> {
9193
type Public = T::AuthorityId;
9294
}
9395

94-
impl<T: Config> pallet_session::OneSessionHandler<T::AccountId> for Module<T> {
96+
impl<T: Config> OneSessionHandler<T::AccountId> for Module<T> {
9597
type Key = T::AuthorityId;
9698

9799
fn on_genesis_session<'a, I: 'a>(validators: I)

primitives/beefy/Cargo.toml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
name = "beefy-primitives"
3+
version = "0.1.0"
4+
authors = ["Parity Technologies <admin@parity.io>"]
5+
edition = "2018"
6+
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
7+
8+
[dependencies]
9+
codec = { version = "2.0.0", package = "parity-scale-codec", default-features = false, features = ["derive"] }
10+
11+
sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
12+
sp-application-crypto = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
13+
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
14+
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
15+
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
16+
17+
[dev-dependencies]
18+
hex-literal = "0.3"
19+
20+
[features]
21+
default = ["std"]
22+
std = [
23+
"codec/std",
24+
"sp-api/std",
25+
"sp-application-crypto/std",
26+
"sp-core/std",
27+
"sp-runtime/std",
28+
"sp-std/std",
29+
]

primitives/beefy/src/commitment.rs

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
2+
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3+
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
use sp_std::{cmp, prelude::*};
18+
19+
use crate::ValidatorSetId;
20+
21+
/// A commitment signed by GRANDPA validators as part of BEEFY protocol.
22+
///
23+
/// The commitment contains a [payload] extracted from the finalized block at height [block_number].
24+
/// GRANDPA validators collect signatures on commitments and a stream of such signed commitments
25+
/// (see [SignedCommitment]) forms the BEEFY protocol.
26+
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
27+
pub struct Commitment<TBlockNumber, TPayload> {
28+
/// The payload being signed.
29+
///
30+
/// This should be some form of cumulative representation of the chain (think MMR root hash).
31+
/// The payload should also contain some details that allow the light client to verify next
32+
/// validator set. The protocol does not enforce any particular format of this data,
33+
/// nor how often it should be present in commitments, however the light client has to be
34+
/// provided with full validator set whenever it performs the transition (i.e. importing first
35+
/// block with [validator_set_id] incremented).
36+
pub payload: TPayload,
37+
38+
/// Finalized block number this commitment is for.
39+
///
40+
/// GRANDPA validators agree on a block they create a commitment for and start collecting
41+
/// signatures. This process is called a round.
42+
/// There might be multiple rounds in progress (depending on the block choice rule), however
43+
/// since the payload is supposed to be cumulative, it is not required to import all
44+
/// commitments.
45+
/// BEEFY light client is expected to import at least one commitment per epoch,
46+
/// but is free to import as many as it requires.
47+
pub block_number: TBlockNumber,
48+
49+
/// BEEFY validator set supposed to sign this commitment.
50+
///
51+
/// Validator set is changing once per epoch. The Light Client must be provided by details about
52+
/// the validator set whenever it's importing first commitment with a new `validator_set_id`.
53+
/// Validator set data MUST be verifiable, for instance using [payload] information.
54+
pub validator_set_id: ValidatorSetId,
55+
}
56+
57+
impl<TBlockNumber, TPayload> cmp::PartialOrd for Commitment<TBlockNumber, TPayload>
58+
where
59+
TBlockNumber: cmp::Ord,
60+
TPayload: cmp::Eq,
61+
{
62+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
63+
Some(self.cmp(other))
64+
}
65+
}
66+
67+
impl<TBlockNumber, TPayload> cmp::Ord for Commitment<TBlockNumber, TPayload>
68+
where
69+
TBlockNumber: cmp::Ord,
70+
TPayload: cmp::Eq,
71+
{
72+
fn cmp(&self, other: &Self) -> cmp::Ordering {
73+
self.validator_set_id
74+
.cmp(&other.validator_set_id)
75+
.then_with(|| self.block_number.cmp(&other.block_number))
76+
}
77+
}
78+
79+
/// A commitment with matching GRANDPA validators' signatures.
80+
#[derive(Clone, Debug, PartialEq, Eq, codec::Encode, codec::Decode)]
81+
pub struct SignedCommitment<TBlockNumber, TPayload, TSignature> {
82+
/// The commitment signatures are collected for.
83+
pub commitment: Commitment<TBlockNumber, TPayload>,
84+
/// GRANDPA validators' signatures for the commitment.
85+
///
86+
/// The length of this `Vec` must match number of validators in the current set (see
87+
/// [Commitment::validator_set_id]).
88+
pub signatures: Vec<Option<TSignature>>,
89+
}
90+
91+
impl<TBlockNumber, TPayload, TSignature> SignedCommitment<TBlockNumber, TPayload, TSignature> {
92+
/// Return the number of collected signatures.
93+
pub fn no_of_signatures(&self) -> usize {
94+
self.signatures.iter().filter(|x| x.is_some()).count()
95+
}
96+
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use super::*;
101+
use codec::Decode;
102+
103+
type TestCommitment = Commitment<u128, String>;
104+
type TestSignedCommitment = SignedCommitment<u128, String, Vec<u8>>;
105+
106+
#[test]
107+
fn commitment_encode_decode() {
108+
// given
109+
let commitment: TestCommitment = Commitment {
110+
payload: "Hello World!".into(),
111+
block_number: 5,
112+
validator_set_id: 0,
113+
};
114+
115+
// when
116+
let encoded = codec::Encode::encode(&commitment);
117+
let decoded = TestCommitment::decode(&mut &*encoded);
118+
119+
// then
120+
assert_eq!(decoded, Ok(commitment));
121+
assert_eq!(
122+
encoded,
123+
hex_literal::hex!("3048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000")
124+
);
125+
}
126+
127+
#[test]
128+
fn signed_commitment_encode_decode() {
129+
// given
130+
let commitment: TestCommitment = Commitment {
131+
payload: "Hello World!".into(),
132+
block_number: 5,
133+
validator_set_id: 0,
134+
};
135+
let signed = SignedCommitment {
136+
commitment,
137+
signatures: vec![None, None, Some(vec![1, 2, 3, 4]), Some(vec![5, 6, 7, 8])],
138+
};
139+
140+
// when
141+
let encoded = codec::Encode::encode(&signed);
142+
let decoded = TestSignedCommitment::decode(&mut &*encoded);
143+
144+
// then
145+
assert_eq!(decoded, Ok(signed));
146+
assert_eq!(
147+
encoded,
148+
hex_literal::hex!(
149+
"3048656c6c6f20576f726c6421050000000000000000000000000000000000000000000000100000011001020304011005060708"
150+
)
151+
);
152+
}
153+
154+
#[test]
155+
fn signed_commitment_count_signatures() {
156+
// given
157+
let commitment: TestCommitment = Commitment {
158+
payload: "Hello World!".into(),
159+
block_number: 5,
160+
validator_set_id: 0,
161+
};
162+
let mut signed = SignedCommitment {
163+
commitment,
164+
signatures: vec![None, None, Some(vec![1, 2, 3, 4]), Some(vec![5, 6, 7, 8])],
165+
};
166+
assert_eq!(signed.no_of_signatures(), 2);
167+
168+
// when
169+
signed.signatures[2] = None;
170+
171+
// then
172+
assert_eq!(signed.no_of_signatures(), 1);
173+
}
174+
175+
#[test]
176+
fn commitment_ordering() {
177+
fn commitment(block_number: u128, validator_set_id: crate::ValidatorSetId) -> TestCommitment {
178+
Commitment {
179+
payload: "Hello World!".into(),
180+
block_number,
181+
validator_set_id,
182+
}
183+
}
184+
185+
// given
186+
let a = commitment(1, 0);
187+
let b = commitment(2, 1);
188+
let c = commitment(10, 0);
189+
let d = commitment(10, 1);
190+
191+
// then
192+
assert!(a < b);
193+
assert!(a < c);
194+
assert!(c < b);
195+
assert!(c < d);
196+
assert!(b < d);
197+
}
198+
}

0 commit comments

Comments
 (0)