Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions collator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ use sp_core::Pair;
use polkadot_primitives::v0::{
BlockId, Hash, Block, DownwardMessage,
BlockData, DutyRoster, HeadData, Id as ParaId,
PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationSchedule,
PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationData,
Collation, CollationInfo, collator_signature_payload,
};
use polkadot_cli::{
Expand Down Expand Up @@ -148,7 +148,7 @@ pub trait ParachainContext: Clone {
fn produce_candidate(
&mut self,
relay_parent: Hash,
global_validation: GlobalValidationSchedule,
global_validation: GlobalValidationData,
local_validation: LocalValidationData,
downward_messages: Vec<DownwardMessage>,
) -> Self::ProduceCandidate;
Expand All @@ -158,7 +158,7 @@ pub trait ParachainContext: Clone {
pub async fn collate<P>(
relay_parent: Hash,
local_id: ParaId,
global_validation: GlobalValidationSchedule,
global_validation: GlobalValidationData,
local_validation_data: LocalValidationData,
downward_messages: Vec<DownwardMessage>,
mut para_context: P,
Expand Down Expand Up @@ -315,7 +315,7 @@ fn build_collator_service<P, C, R, Extrinsic>(

let work = future::lazy(move |_| {
let api = client.runtime_api();
let global_validation = try_fr!(api.global_validation_schedule(&id));
let global_validation = try_fr!(api.global_validation_data(&id));
let local_validation = match try_fr!(api.local_validation_data(&id, para_id)) {
Some(local_validation) => local_validation,
None => return future::Either::Left(future::ok(())),
Expand Down Expand Up @@ -477,7 +477,7 @@ mod tests {
fn produce_candidate(
&mut self,
_relay_parent: Hash,
_global: GlobalValidationSchedule,
_global: GlobalValidationData,
_local_validation: LocalValidationData,
_: Vec<DownwardMessage>,
) -> Self::ProduceCandidate {
Expand Down
4 changes: 2 additions & 2 deletions network/src/protocol/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use polkadot_primitives::v0::{
Block,
Id as ParaId, Chain, DutyRoster, ParachainHost, ValidatorId,
Retriable, CollatorId, AbridgedCandidateReceipt,
GlobalValidationSchedule, LocalValidationData, ErasureChunk, SigningContext,
GlobalValidationData, LocalValidationData, ErasureChunk, SigningContext,
PoVBlock, BlockData, ValidationCode,
};
use polkadot_validation::{SharedTable, TableRouter};
Expand Down Expand Up @@ -180,7 +180,7 @@ sp_api::mock_impl_runtime_apis! {
Some(ValidationCode(Vec::new()))
}

fn global_validation_schedule() -> GlobalValidationSchedule {
fn global_validation_data() -> GlobalValidationData {
Default::default()
}

Expand Down
20 changes: 10 additions & 10 deletions node/core/backing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ impl CandidateBackingJob {
with_commitments: impl FnOnce(CandidateCommitments) -> Result<T, E>,
) -> Result<Result<T, E>, Error> {
let omitted_validation = OmittedValidationData {
global_validation: outputs.global_validation_schedule,
global_validation: outputs.global_validation_data,
local_validation: outputs.local_validation_data,
};

Expand Down Expand Up @@ -773,7 +773,7 @@ mod tests {
use futures::{executor, future, Future};
use polkadot_primitives::v1::{
AssignmentKind, BlockData, CandidateCommitments, CollatorId, CoreAssignment, CoreIndex,
LocalValidationData, GlobalValidationSchedule, GroupIndex, HeadData,
LocalValidationData, GlobalValidationData, GroupIndex, HeadData,
ValidatorPair, ValidityAttestation,
};
use polkadot_subsystem::{
Expand All @@ -792,7 +792,7 @@ mod tests {
keystore: KeyStorePtr,
validators: Vec<Sr25519Keyring>,
validator_public: Vec<ValidatorId>,
global_validation_schedule: GlobalValidationSchedule,
global_validation_data: GlobalValidationData,
local_validation_data: LocalValidationData,
roster: SchedulerRoster,
head_data: HashMap<ParaId, HeadData>,
Expand Down Expand Up @@ -877,7 +877,7 @@ mod tests {
validation_code_hash: Default::default(),
};

let global_validation_schedule = GlobalValidationSchedule {
let global_validation_data = GlobalValidationData {
max_code_size: 1000,
max_head_data_size: 1000,
block_number: Default::default(),
Expand All @@ -891,7 +891,7 @@ mod tests {
roster,
head_data,
local_validation_data,
global_validation_schedule,
global_validation_data,
signing_context,
relay_parent,
}
Expand Down Expand Up @@ -921,7 +921,7 @@ mod tests {

fn make_erasure_root(test: &TestState, pov: PoV) -> Hash {
let omitted_validation = OmittedValidationData {
global_validation: test.global_validation_schedule.clone(),
global_validation: test.global_validation_data.clone(),
local_validation: test.local_validation_data.clone(),
};

Expand Down Expand Up @@ -1048,7 +1048,7 @@ mod tests {
) if pov == pov && &c == candidate.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_schedule: test_state.global_validation_schedule,
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
Expand Down Expand Up @@ -1160,7 +1160,7 @@ mod tests {
) if pov == pov && &c == candidate_a.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_schedule: test_state.global_validation_schedule,
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
Expand Down Expand Up @@ -1281,7 +1281,7 @@ mod tests {
) if pov == pov && &c == candidate_a.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_schedule: test_state.global_validation_schedule,
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
Expand Down Expand Up @@ -1438,7 +1438,7 @@ mod tests {
) if pov == pov && &c == candidate_b.descriptor() => {
tx.send(Ok(
ValidationResult::Valid(ValidationOutputs {
global_validation_schedule: test_state.global_validation_schedule,
global_validation_data: test_state.global_validation_data,
local_validation_data: test_state.local_validation_data,
head_data: expected_head_data.clone(),
upward_messages: Vec::new(),
Expand Down
4 changes: 2 additions & 2 deletions node/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use parity_scale_codec::{Decode, Encode};
use polkadot_primitives::v1::{
Hash, CommittedCandidateReceipt, CandidateReceipt, CompactStatement,
EncodeAs, Signed, SigningContext, ValidatorIndex, ValidatorId,
UpwardMessage, Balance, ValidationCode, GlobalValidationSchedule, LocalValidationData,
UpwardMessage, Balance, ValidationCode, GlobalValidationData, LocalValidationData,
HeadData,
};
use polkadot_statement_table::{
Expand Down Expand Up @@ -118,7 +118,7 @@ pub struct ValidationOutputs {
/// The head-data produced by validation.
pub head_data: HeadData,
/// The global validation schedule.
pub global_validation_schedule: GlobalValidationSchedule,
pub global_validation_data: GlobalValidationData,
/// The local validation data.
pub local_validation_data: LocalValidationData,
/// Upward messages to the relay chain.
Expand Down
4 changes: 2 additions & 2 deletions parachain/test-parachains/adder/collator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use sp_core::Pair;
use codec::{Encode, Decode};
use primitives::v0::{
Hash, DownwardMessage,
HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationSchedule,
HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationData,
};
use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli};
use parking_lot::Mutex;
Expand Down Expand Up @@ -58,7 +58,7 @@ impl ParachainContext for AdderContext {
fn produce_candidate(
&mut self,
_relay_parent: Hash,
_global_validation: GlobalValidationSchedule,
_global_validation: GlobalValidationData,
local_validation: LocalValidationData,
_: Vec<DownwardMessage>,
) -> Self::ProduceCandidate
Expand Down
8 changes: 4 additions & 4 deletions primitives/src/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pub struct DutyRoster {
/// These are global parameters that apply to all parachain candidates in a block.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct GlobalValidationSchedule<N = BlockNumber> {
pub struct GlobalValidationData<N = BlockNumber> {
/// The maximum code size permitted, in bytes.
pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes.
Expand Down Expand Up @@ -278,7 +278,7 @@ pub struct CandidateReceipt<H = Hash, N = BlockNumber> {
/// The hash of the PoV-block.
pub pov_block_hash: H,
/// The global validation schedule.
pub global_validation: GlobalValidationSchedule<N>,
pub global_validation: GlobalValidationData<N>,
/// The local validation data.
pub local_validation: LocalValidationData<N>,
/// Commitments made as a result of validation.
Expand Down Expand Up @@ -352,7 +352,7 @@ impl Ord for CandidateReceipt {
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct OmittedValidationData<N = BlockNumber> {
/// The global validation schedule.
pub global_validation: GlobalValidationSchedule<N>,
pub global_validation: GlobalValidationData<N>,
/// The local validation data.
pub local_validation: LocalValidationData<N>,
}
Expand Down Expand Up @@ -762,7 +762,7 @@ sp_api::decl_runtime_apis! {
fn active_parachains() -> Vec<(Id, Option<(CollatorId, Retriable)>)>;
/// Get the global validation schedule that all parachains should
/// be validated under.
fn global_validation_schedule() -> GlobalValidationSchedule;
fn global_validation_data() -> GlobalValidationData;
/// Get the local validation data for a particular parachain.
fn local_validation_data(id: Id) -> Option<LocalValidationData>;
/// Get the given parachain's head code blob.
Expand Down
61 changes: 49 additions & 12 deletions primitives/src/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,33 +60,50 @@ pub const INCLUSION_INHERENT_IDENTIFIER: InherentIdentifier = *b"inclusn0";
pub fn collator_signature_payload<H: AsRef<[u8]>>(
relay_parent: &H,
para_id: &Id,
validation_data_hash: &Hash,
pov_hash: &Hash,
) -> [u8; 68] {
) -> [u8; 100] {
// 32-byte hash length is protected in a test below.
let mut payload = [0u8; 68];
let mut payload = [0u8; 100];

payload[0..32].copy_from_slice(relay_parent.as_ref());
u32::from(*para_id).using_encoded(|s| payload[32..32 + s.len()].copy_from_slice(s));
payload[36..68].copy_from_slice(pov_hash.as_ref());
payload[36..68].copy_from_slice(validation_data_hash.as_ref());
payload[68..100].copy_from_slice(pov_hash.as_ref());
Comment on lines -64 to +72
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These numeric literals feel ripe for extraction into constants. Alternately, would SCALE do the right thing with (relay_parent.as_ref(), validation_data_hash.as_ref(), pov_hash.as_ref()).encode()?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would, and maybe this is premature optimization, but I wanted to do this on the stack as this is called within the runtime and allocations might be expensive.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why specifically would constants help here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(let's address this as follow-on, since the status quo is "no constants")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on the stack is a plausible reason to copy from slices instead of using higher-level methods.

Constants help because they make typos into compiler errors. Contrast these approaches:

payload[0..32].copy_from_slice(relay_parent.as_ref());
u32::from(*para_id).using_encoded(|s| payload[32..32 + s.len()].copy_from_slice(s));
payload[36..68].copy_from_slice(pov_hash.as_ref());
payload[36..68].copy_from_slice(validation_data_hash.as_ref());
payload[66..100].copy_from_slice(pov_hash.as_ref()); // subtle bug which will panic
payload[0..PARA_ID].copy_from_slice(relay_parent.as_ref());
u32::from(*para_id).using_encoded(|s| payload[PARA_ID..PARA_ID + s.len()].copy_from_slice(s));
payload[POV_HASH..VALIDATION_DATA_HASH].copy_from_slice(pov_hash.as_ref());
payload[VALIDATION_DATA_HASH..POV_HASH].copy_from_slice(validation_data_hash.as_ref());
payload[POV_HASH..FINAL_SIZE].copy_from_slice(pov_hash.as_ref());

A reasonable test suite would catch the panic in this case, but that's not true in every situation. It just feels like a good practice to name applicable numeric constants and magic numbers instead of typing them by hand all the time.

Copy link
Copy Markdown
Contributor Author

@rphmeier rphmeier Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also just as easily mess up the definition of POV_HASH. It doesn't guarantee anything except that when an offset is used twice, it's not mis-typed the second time.

I am in favor of extracting literals to constants when they are not hyper-localized. If there is a constant that an entire module needs to make use of, then it doesn't make sense to use a literal. But this is hyper-localized code within a single function that is less than 10 lines of implementation.


payload
}

fn check_collator_signature<H: AsRef<[u8]>>(
relay_parent: &H,
para_id: &Id,
validation_data_hash: &Hash,
pov_hash: &Hash,
collator: &CollatorId,
signature: &CollatorSignature,
) -> Result<(),()> {
let payload = collator_signature_payload(relay_parent, para_id, pov_hash);
let payload = collator_signature_payload(
relay_parent,
para_id,
validation_data_hash,
pov_hash,
);

if signature.verify(&payload[..], collator) {
Ok(())
} else {
Err(())
}
}

/// Compute the `validation_data_hash` from global & local validation data.
pub fn validation_data_hash<N: Encode>(
global: &GlobalValidationData<N>,
local: &LocalValidationData<N>,
) -> Hash {
BlakeTwo256::hash_of(&(global, local))
}

/// A unique descriptor of the candidate receipt.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
Expand All @@ -97,11 +114,16 @@ pub struct CandidateDescriptor<H = Hash> {
pub relay_parent: H,
/// The collator's sr25519 public key.
pub collator: CollatorId,
/// Signature on blake2-256 of components of this receipt:
/// The parachain index, the relay parent, and the pov_hash.
pub signature: CollatorSignature,
/// The blake2-256 hash of the validation data. This is extra data derived from
/// relay-chain state which may vary based on bitfields included before the candidate.
/// Thus it cannot be derived entirely from the relay-parent.
pub validation_data_hash: Hash,
/// The blake2-256 hash of the pov.
pub pov_hash: Hash,
/// Signature on blake2-256 of components of this receipt:
/// The parachain index, the relay parent, the validation data hash, and the pov_hash.
pub signature: CollatorSignature,

}

impl<H: AsRef<[u8]>> CandidateDescriptor<H> {
Expand All @@ -110,6 +132,7 @@ impl<H: AsRef<[u8]>> CandidateDescriptor<H> {
check_collator_signature(
&self.relay_parent,
&self.para_id,
&self.validation_data_hash,
&self.pov_hash,
&self.collator,
&self.signature,
Expand Down Expand Up @@ -146,7 +169,7 @@ pub struct FullCandidateReceipt<H = Hash, N = BlockNumber> {
/// The inner candidate receipt.
pub inner: CandidateReceipt<H>,
/// The global validation schedule.
pub global_validation: GlobalValidationSchedule<N>,
pub global_validation: GlobalValidationData<N>,
/// The local validation data.
pub local_validation: LocalValidationData<N>,
}
Expand Down Expand Up @@ -232,7 +255,7 @@ pub struct LocalValidationData<N = BlockNumber> {
/// These are global parameters that apply to all candidates in a block.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct GlobalValidationSchedule<N = BlockNumber> {
pub struct GlobalValidationData<N = BlockNumber> {
/// The maximum code size permitted, in bytes.
pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes.
Expand Down Expand Up @@ -465,7 +488,7 @@ impl CoreAssignment {
#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
pub struct OmittedValidationData {
/// The global validation schedule.
pub global_validation: GlobalValidationSchedule,
pub global_validation: GlobalValidationData,
/// The local validation data.
pub local_validation: LocalValidationData,
}
Expand Down Expand Up @@ -636,9 +659,9 @@ sp_api::decl_runtime_apis! {
/// cores can have paras assigned to them.
fn availability_cores() -> Vec<CoreState<N>>;

/// Yields the GlobalValidationSchedule. This applies to all para candidates with the
/// Yields the GlobalValidationData. This applies to all para candidates with the
/// relay-parent equal to the block in which context this is invoked in.
fn global_validation_schedule() -> GlobalValidationSchedule<N>;
fn global_validation_data() -> GlobalValidationData<N>;

/// Yields the LocalValidationData for the given ParaId along with an assumption that
/// should be used if the para currently occupies a core.
Expand Down Expand Up @@ -696,4 +719,18 @@ mod tests {
assert_eq!(info.next_rotation_at(), 0);
assert_eq!(info.last_rotation_at(), 0);
}

#[test]
fn collator_signature_payload_is_valid() {
// if this fails, collator signature verification code has to be updated.
let h = Hash::default();
assert_eq!(h.as_ref().len(), 32);

let _payload = collator_signature_payload(
&Hash::from([1; 32]),
&5u32.into(),
&Hash::from([2; 32]),
&Hash::from([3; 32]),
);
}
}
4 changes: 2 additions & 2 deletions roadmap/implementers-guide/src/runtime-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ enum CoreState {

## Global Validation Schedule

Yields the [`GlobalValidationSchedule`](../types/candidate.md#globalvalidationschedule) at the state of a given block. This applies to all para candidates with the relay-parent equal to that block.
Yields the [`GlobalValidationData`](../types/candidate.md#globalvalidationschedule) at the state of a given block. This applies to all para candidates with the relay-parent equal to that block.

```rust
fn global_validation_schedule(at: Block) -> GlobalValidationSchedule;
fn global_validation_data(at: Block) -> GlobalValidationData;
```

## Local Validation Data
Expand Down
3 changes: 3 additions & 0 deletions roadmap/implementers-guide/src/runtime/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ fn update_configuration(f: impl FnOnce(&mut HostConfiguration)) {
*pending = Some(x);
})
}

/// Get the GlobalValidationData, assuming the context is the parent block.
fn global_validation_data() -> GlobalValidationData;
```

## Entry-points
Expand Down
1 change: 1 addition & 0 deletions roadmap/implementers-guide/src/runtime/inclusion.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ All failed checks should lead to an unrecoverable error making the block invalid
1. check that each candidate corresponds to a scheduled core and that they are ordered in the same order the cores appear in assignments in `scheduled`.
1. check that `scheduled` is sorted ascending by `CoreIndex`, without duplicates.
1. check that there is no candidate pending availability for any scheduled `ParaId`.
1. check that each candidate's `validation_data_hash` corresponds to a `(LocalValidationData, GlobalValidationData)` computed from the current state.
1. If the core assignment includes a specific collator, ensure the backed candidate is issued by that collator.
1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_frequency` of `Paras::last_code_upgrade(para_id, true)`, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID.
1. Check the collator's signature on the candidate data.
Expand Down
Loading