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

New parachain runtime skeleton #1158

Merged
merged 42 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
05caf14
file structure and initializer skeleton
rphmeier May 26, 2020
0703ffd
ensure session changes happen before initialization
rphmeier May 26, 2020
11da041
add a couple tests for initializer flow
rphmeier May 26, 2020
00d4d60
integrate with session handling
rphmeier May 26, 2020
e07e656
configuration update logic
rphmeier May 26, 2020
af66290
configuration methods
rphmeier May 26, 2020
48ddd60
move test mock to its own module
rphmeier May 26, 2020
33dcdb1
integrate configuration into initializer
rphmeier May 26, 2020
0b228e9
add note about initialization order
rphmeier May 26, 2020
f8a8916
integrate configuration module into mock
rphmeier May 26, 2020
2d0c60a
add some tests for config module
rphmeier May 26, 2020
c1d854f
paras module storage
rphmeier May 27, 2020
b346d47
implement paras session change operation
rphmeier May 27, 2020
3c2dd39
amend past code pruning to fully cover acceptance period
rphmeier May 27, 2020
8183638
update guide again
rphmeier May 27, 2020
26b8870
do pruning of historical validation code
rphmeier May 27, 2020
c32759b
add weight to initialization
rphmeier May 27, 2020
ec0a15d
integrate into mock & leave notes for next session
rphmeier May 27, 2020
182fbf4
clean up un-ended sentence
rphmeier May 27, 2020
a3b89f7
alter test to account for double index in past code meta
rphmeier May 27, 2020
3b82a87
port over code-at logic test
rphmeier May 27, 2020
1a585a6
clarify checking for conflicting code upgrades
rphmeier May 27, 2020
10fe902
add genesis for paras, include in mock, ensure incoming paras are pro…
rphmeier May 27, 2020
a05a4b9
note on return value of `validation_code_at`
rphmeier May 27, 2020
f77326f
implement paras routines from implementor's guide
rphmeier May 28, 2020
fdc3631
bring over some existing tests and begin porting
rphmeier May 28, 2020
a58b349
port over code upgrade tests
rphmeier May 28, 2020
e671590
test parachain registration
rphmeier May 28, 2020
11c8c3c
test code_at with intermediate block
rphmeier May 28, 2020
0116cc4
fix warnings
rphmeier May 28, 2020
b608d34
Merge branch 'master' into rh-para-runtime-skeleton
rphmeier May 28, 2020
e0f1a71
clean up docs and extract to separate struct
rphmeier May 28, 2020
4fce5b5
adjust implementor's guide to include replacementtimes
rphmeier May 29, 2020
1297674
kill stray println
rphmeier May 29, 2020
e486ce3
rename expected_at to applied_after
rphmeier May 30, 2020
b50e6d2
rewrite ParaPastCodeMeta to avoid reversal
rphmeier May 30, 2020
ffe66b1
clarify and test interface of validation_code_at
rphmeier May 30, 2020
829dc2e
make FutureCode optional
rphmeier May 30, 2020
5a8efc8
Merge branch 'master' into rh-para-runtime-skeleton
rphmeier Jun 1, 2020
a01bafd
rename do_old_code_pruning
rphmeier Jun 1, 2020
2a079cc
add comment on Option<()> to answer FAQ
rphmeier Jun 1, 2020
4adc554
address some more grumbles
rphmeier Jun 1, 2020
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
14 changes: 13 additions & 1 deletion roadmap/implementors-guide/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,18 @@ It's also responsible for managing parachain validation code upgrades as well as

Utility structs:
```rust
// the two key times necessary to track for every code replacement.
pub struct ReplacementTimes {
/// The relay-chain block number that the code upgrade was expected to be activated.
/// This is when the code change occurs from the para's perspective - after the
/// first parablock included with a relay-parent with number >= this value.
expected_at: BlockNumber,
/// The relay-chain block number at which the parablock activating the code upgrade was
/// actually included. This means considered included and available, so this is the time at which
/// that parablock enters the acceptance period in this fork of the relay-chain.
activated_at: BlockNumber,
}

/// Metadata used to track previous parachain validation code that we keep in
/// the state.
pub struct ParaPastCodeMeta {
Expand All @@ -480,7 +492,7 @@ pub struct ParaPastCodeMeta {
// of historic code in historic contexts, whereas the second is used to do
// pruning on an accurate timeframe. These can be used as indices
// into the `PastCode` map along with the `ParaId` to fetch the code itself.
upgrade_times: Vec<(BlockNumber, BlockNumber)>,
upgrade_times: Vec<ReplacementTimes>,
// This tracks the highest pruned code-replacement, if any.
last_pruned: Option<BlockNumber>,
}
Expand Down
71 changes: 49 additions & 22 deletions runtime/parachains/src/paras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,31 @@ use serde::{Serialize, Deserialize};

pub trait Trait: system::Trait + configuration::Trait { }

// the two key times necessary to track for every code replacement.
#[derive(Default, Encode, Decode)]
#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
pub struct ReplacementTimes<N> {
/// The relay-chain block number that the code upgrade was expected to be activated.
/// This is when the code change occurs from the para's perspective - after the
/// first parablock included with a relay-parent with number >= this value.
expected_at: N,
/// The relay-chain block number at which the parablock activating the code upgrade was
/// actually included. This means considered included and available, so this is the time at which
/// that parablock enters the acceptance period in this fork of the relay-chain.
activated_at: N,
}

/// Metadata used to track previous parachain validation code that we keep in
/// the state.
#[derive(Default, Encode, Decode)]
#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
pub struct ParaPastCodeMeta<N> {
// Block numbers where the code was "technically" replaced and the block number at
// which the code was actually replaced. These can be used as indices
// Block numbers where the code was expected to be replaced and where the code
// was actually replaced, respectively. The first is used to do accurate lookups
// of historic code in historic contexts, whereas the second is used to do
// pruning on an accurate timeframe. These can be used as indices
// into the `PastCode` map along with the `ParaId` to fetch the code itself.
upgrade_times: Vec<(N, N)>,
upgrade_times: Vec<ReplacementTimes<N>>,
// This tracks the highest pruned code-replacement, if any.
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
last_pruned: Option<N>,
}
Expand All @@ -60,38 +76,42 @@ enum UseCodeAt<N> {
// Use the current code.
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
Current,
// Use the code that was replaced at the given block number.
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
// This is an inclusive endpoint - a parablock in the context of a relay-chain block on this fork
// with number N should use the code that is replaced at N.
ReplacedAt(N),
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
}

impl<N: Ord + Copy> ParaPastCodeMeta<N> {
// note a replacement has occurred at a given block number.
fn note_replacement(&mut self, at: N, included_at: N) {
self.upgrade_times.insert(0, (at, included_at))
fn note_replacement(&mut self, expected_at: N, activated_at: N) {
self.upgrade_times.insert(0, ReplacementTimes { expected_at, activated_at })
}

// Yields the block number of the code that should be used for validating at
// the given block number.
//
//
//
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
// a return value of `None` means that there is no code we are aware of that
// should be used to validate at the given height.
#[allow(unused)]
fn code_at(&self, at: N) -> Option<UseCodeAt<N>> {
fn code_at(&self, para_at: N) -> Option<UseCodeAt<N>> {
// The `PastCode` map stores the code which was replaced at `t`.
let end_position = self.upgrade_times.iter().position(|&t| t.0 < at);
let end_position = self.upgrade_times.iter().position(|t| t.expected_at < para_at);
if let Some(end_position) = end_position {
Some(if end_position != 0 {
// `end_position` gives us the replacement time where the code used at `at`
// `end_position` gives us the replacement time where the code used at `para_at`
// was set. But that code has been replaced: `end_position - 1` yields
// that index.
UseCodeAt::ReplacedAt(self.upgrade_times[end_position - 1].0)
UseCodeAt::ReplacedAt(self.upgrade_times[end_position - 1].expected_at)
} else {
// the most recent tracked replacement is before `at`.
// this means that the code put in place then (i.e. the current code)
// is correct for validating at `at`.
UseCodeAt::Current
})
} else {
if self.last_pruned.as_ref().map_or(true, |&n| n < at) {
if self.last_pruned.as_ref().map_or(true, |&n| n < para_at) {
// Our `last_pruned` is before `at`, so we still have the code!
// but no code upgrade entries found before the `at` parameter.
//
Expand All @@ -100,7 +120,7 @@ impl<N: Ord + Copy> ParaPastCodeMeta<N> {
// 2. there are non-pruned upgrade logs all after `at`.
// in this case use the oldest upgrade log.
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
Some(self.upgrade_times.last()
.map(|n| UseCodeAt::ReplacedAt(n.0))
.map(|n| UseCodeAt::ReplacedAt(n.expected_at))
.unwrap_or(UseCodeAt::Current)
)
} else {
Expand All @@ -113,29 +133,33 @@ impl<N: Ord + Copy> ParaPastCodeMeta<N> {
// The block at which the most recently tracked code change occurred, from the perspective
// of the para.
fn most_recent_change(&self) -> Option<N> {
self.upgrade_times.first().map(|x| x.0.clone())
self.upgrade_times.first().map(|x| x.expected_at.clone())
}

// prunes all code upgrade logs occurring at or before `max`.
// note that code replaced at `x` is the code used to validate all blocks before
// `x`. Thus, `max` should be outside of the slashing window when this is invoked.
//
// Since we don't want to prune anything inside the acceptance period, and the parablock only
// enters the acceptance period after being included, we prune based on the activation height of
// the code change, not the expected height of the code change.
//
// returns an iterator of block numbers at which code was replaced, where the replaced
// code should be now pruned, in ascending order.
fn prune_up_to(&'_ mut self, max: N) -> impl Iterator<Item=N> + '_ {
let drained = match self.upgrade_times.iter().position(|&t| t.1 <= max) {
let drained = match self.upgrade_times.iter().position(|t| t.activated_at <= max) {
None => {
// this is a no-op `drain` - desired because all
// logged code upgrades occurred after `max`.
self.upgrade_times.drain(self.upgrade_times.len()..).rev()
}
Some(pos) => {
self.last_pruned = Some(self.upgrade_times[pos].0);
self.last_pruned = Some(self.upgrade_times[pos].expected_at);
self.upgrade_times.drain(pos..).rev()
}
};

drained.map(|(replaced, _)| replaced)
drained.map(|times| times.expected_at)
}
}

Expand Down Expand Up @@ -212,7 +236,6 @@ fn build<T: Trait>(config: &GenesisConfig<T>) {
Parachains::put(&parachains);

for (id, genesis_args) in &config.paras {
println!("Initializing genesis for para {:?}", id);
<Module<T> as Store>::CurrentCode::insert(&id, &genesis_args.validation_code);
<Module<T> as Store>::Heads::insert(&id, &genesis_args.genesis_head);
}
Expand Down Expand Up @@ -532,6 +555,10 @@ mod tests {
}
}

fn upgrade_at(expected_at: BlockNumber, activated_at: BlockNumber) -> ReplacementTimes<BlockNumber> {
ReplacementTimes { expected_at, activated_at }
}

#[test]
fn para_past_code_meta_gives_right_code() {
let mut past_code = ParaPastCodeMeta::default();
Expand Down Expand Up @@ -568,15 +595,15 @@ mod tests {

assert_eq!(past_code.prune_up_to(10).collect::<Vec<_>>(), vec![10]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![(30, 35), (20, 25)],
upgrade_times: vec![upgrade_at(30, 35), upgrade_at(20, 25)],
last_pruned: Some(10),
});

assert!(past_code.prune_up_to(21).collect::<Vec<_>>().is_empty());

assert_eq!(past_code.prune_up_to(26).collect::<Vec<_>>(), vec![20]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![(30, 35)],
upgrade_times: vec![upgrade_at(30, 35)],
last_pruned: Some(20),
});

Expand All @@ -585,13 +612,13 @@ mod tests {
past_code.note_replacement(60, 66);

assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![(60, 66), (50, 53), (40, 42), (30, 35)],
upgrade_times: vec![upgrade_at(60, 66), upgrade_at(50, 53), upgrade_at(40, 42), upgrade_at(30, 35)],
last_pruned: Some(20),
});

assert_eq!(past_code.prune_up_to(60).collect::<Vec<_>>(), vec![30, 40, 50]);
assert_eq!(past_code, ParaPastCodeMeta {
upgrade_times: vec![(60, 66)],
upgrade_times: vec![upgrade_at(60, 66)],
last_pruned: Some(50),
});

Expand Down Expand Up @@ -697,14 +724,14 @@ mod tests {
assert_eq!(
Paras::past_code_meta(&id_a),
ParaPastCodeMeta {
upgrade_times: vec![(10, 12)],
upgrade_times: vec![upgrade_at(10, 12)],
last_pruned: None,
}
);
assert_eq!(
Paras::past_code_meta(&id_b),
ParaPastCodeMeta {
upgrade_times: vec![(20, 23)],
upgrade_times: vec![upgrade_at(20, 23)],
last_pruned: None,
}
);
Expand Down