-
Notifications
You must be signed in to change notification settings - Fork 241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement block tree for the domain v2 #1650
Merged
Merged
Changes from 1 commit
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
cfac1fa
Adjust the generate_genesis_state_root host function to remove the un…
NingLin-P 2a55e3b
Track successfully submitted bundles for all domains as now pallet-do…
NingLin-P cc7cb2a
Add domain primitives for the domain v2 architecture
NingLin-P 458a309
Add block tree specific runtime config
NingLin-P b3e96dd
Impl block tree for the domain v2
NingLin-P 95b61cc
Generate and import the genesis receipt to the block tree upon domain…
NingLin-P fb18078
Revise and add test utilities for upcomming block tree test
NingLin-P 4b92468
Add block tree tests
NingLin-P 520257e
Apply review suggestions
NingLin-P 69302ff
Apply review suggestions
NingLin-P b07e070
Merge branch 'main' of github.com:subspace/subspace into block-tree-pr
NingLin-P 3c45f01
Fix domain tests
NingLin-P 36297c1
Apply review suggestions
NingLin-P 5cce800
Merge branch 'main' of github.com:subspace/subspace into block-tree-pr
NingLin-P d946795
Merge branch 'main' of github.com:subspace/subspace into block-tree-pr
NingLin-P File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
//! Domain block tree | ||
|
||
use crate::{ | ||
BlockTree, Config, DomainBlocks, ExecutionInbox, ExecutionReceiptOf, HeadReceiptNumber, | ||
}; | ||
use codec::{Decode, Encode}; | ||
use frame_support::{ensure, PalletError}; | ||
use scale_info::TypeInfo; | ||
use sp_core::Get; | ||
use sp_domains::v2::ExecutionReceipt; | ||
use sp_domains::{DomainId, OperatorId}; | ||
use sp_runtime::traits::{CheckedSub, One, Saturating, Zero}; | ||
use sp_std::cmp::Ordering; | ||
use sp_std::vec::Vec; | ||
|
||
/// Block tree specific errors | ||
#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)] | ||
pub enum Error { | ||
InvalidExtrinsicsRoots, | ||
UnknownParentBlockReceipt, | ||
BuiltOnUnknownConsensusBlock, | ||
InFutureReceipt, | ||
PrunedReceipt, | ||
ChallengeGenesisReceipt, | ||
ExceedMaxBlockTreeFork, | ||
UnexpectedReceiptType, | ||
MaxHeadDomainNumber, | ||
} | ||
|
||
#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] | ||
pub struct DomainBlock<Number, Hash, DomainNumber, DomainHash, Balance> { | ||
/// The full ER for this block. | ||
pub execution_receipt: ExecutionReceipt<Number, Hash, DomainNumber, DomainHash, Balance>, | ||
/// A set of all operators who have committed to this ER within a bundle. Used to determine who to | ||
/// slash if a fraudulent branch of the `block_tree` is pruned. | ||
/// | ||
/// NOTE: there may be duplicated operator id as an operator can submit multiple bundles with the | ||
/// same head receipt to a consensus block. | ||
pub operator_ids: Vec<OperatorId>, | ||
} | ||
|
||
/// The type of receipt regarding to its freshness | ||
#[derive(Debug, PartialEq, Eq)] | ||
pub(crate) enum ReceiptType { | ||
// New head receipt that extend the longest branch | ||
NewHead, | ||
// Receipt that comfirms the current head receipt | ||
CurrentHead, | ||
// Receipt that creates a new branch of the block tree | ||
NewBranch, | ||
// Receipt that is newer than the head receipt but does not extend the head receipt | ||
InFuture, | ||
// Receipt that comfirm a non-head receipt | ||
Stale, | ||
// Receipt that already been pruned | ||
Pruned, | ||
} | ||
|
||
/// Get the receipt type of the given receipt based on the current block tree state | ||
pub(crate) fn execution_receipt_type<T: Config>( | ||
domain_id: DomainId, | ||
execution_receipt: &ExecutionReceiptOf<T>, | ||
) -> ReceiptType { | ||
let receipt_number = execution_receipt.domain_block_number; | ||
let head_receipt_number = HeadReceiptNumber::<T>::get(domain_id); | ||
|
||
match receipt_number.cmp(&head_receipt_number.saturating_add(One::one())) { | ||
Ordering::Greater => ReceiptType::InFuture, | ||
Ordering::Equal => ReceiptType::NewHead, | ||
liuchengxu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ordering::Less => { | ||
vedhavyas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let oldest_receipt_number = | ||
head_receipt_number.saturating_sub(T::BlockTreePruningDepth::get()); | ||
let already_exist = | ||
BlockTree::<T>::get(domain_id, receipt_number).contains(&execution_receipt.hash()); | ||
|
||
if receipt_number < oldest_receipt_number { | ||
// Receipt already pruned | ||
ReceiptType::Pruned | ||
} else if !already_exist { | ||
// Create new branch | ||
ReceiptType::NewBranch | ||
} else if receipt_number == head_receipt_number { | ||
// Add comfirm to the current head receipt | ||
ReceiptType::CurrentHead | ||
} else { | ||
// Add comfirm to a non-head receipt | ||
ReceiptType::Stale | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Verify the execution receipt | ||
pub(crate) fn verify_execution_receipt<T: Config>( | ||
domain_id: DomainId, | ||
execution_receipt: &ExecutionReceiptOf<T>, | ||
) -> Result<(), Error> { | ||
let ExecutionReceipt { | ||
consensus_block_number, | ||
consensus_block_hash, | ||
domain_block_number, | ||
block_extrinsics_roots, | ||
parent_domain_block_receipt_hash, | ||
.. | ||
} = execution_receipt; | ||
|
||
if !domain_block_number.is_zero() { | ||
let execution_inbox = ExecutionInbox::<T>::get(domain_id, domain_block_number); | ||
ensure!( | ||
!block_extrinsics_roots.is_empty() && *block_extrinsics_roots == execution_inbox, | ||
Error::InvalidExtrinsicsRoots | ||
); | ||
} | ||
|
||
let excepted_consensus_block_hash = | ||
frame_system::Pallet::<T>::block_hash(consensus_block_number); | ||
ensure!( | ||
*consensus_block_hash == excepted_consensus_block_hash, | ||
Error::BuiltOnUnknownConsensusBlock | ||
); | ||
|
||
if let Some(parent_block_number) = domain_block_number.checked_sub(&One::one()) { | ||
let parent_block_exist = BlockTree::<T>::get(domain_id, parent_block_number) | ||
.contains(parent_domain_block_receipt_hash); | ||
ensure!(parent_block_exist, Error::UnknownParentBlockReceipt); | ||
} | ||
|
||
match execution_receipt_type::<T>(domain_id, execution_receipt) { | ||
ReceiptType::InFuture => { | ||
log::error!( | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"Unexpected in future receipt {execution_receipt:?}, which should result in \ | ||
`UnknownParentBlockReceipt` error as it parent receipt is missing" | ||
); | ||
Err(Error::InFutureReceipt) | ||
} | ||
ReceiptType::Pruned => { | ||
log::error!( | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"Unexpected pruned receipt {execution_receipt:?}, which should result in \ | ||
`InvalidExtrinsicsRoots` error as its `ExecutionInbox` is pruned at the same time" | ||
); | ||
Err(Error::PrunedReceipt) | ||
} | ||
// The genesis receipt is generated and added to the block tree by the runtime upon domain | ||
// instantiation, thus it is unchallengeable and must always be the same. | ||
ReceiptType::NewBranch if domain_block_number.is_zero() => { | ||
Err(Error::ChallengeGenesisReceipt) | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
ReceiptType::NewHead | ||
| ReceiptType::NewBranch | ||
| ReceiptType::CurrentHead | ||
| ReceiptType::Stale => Ok(()), | ||
liuchengxu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
/// Process the execution receipt to add it to the block tree | ||
/// | ||
/// NOTE: only `NewHead`, `NewBranch` and `CurrentHead` type of receipt is expected | ||
/// for this function, passing other type of receipt will result in an `UnexpectedReceiptType` | ||
/// error. | ||
pub(crate) fn process_execution_receipt<T: Config>( | ||
domain_id: DomainId, | ||
submitter: OperatorId, | ||
execution_receipt: ExecutionReceiptOf<T>, | ||
receipt_type: ReceiptType, | ||
) -> Result<(), Error> { | ||
let er_hash = execution_receipt.hash(); | ||
match receipt_type { | ||
er_type @ ReceiptType::NewHead | er_type @ ReceiptType::NewBranch => { | ||
vedhavyas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Construct and add a new domain block to the block tree | ||
let domain_block_number = execution_receipt.domain_block_number; | ||
let domain_block = DomainBlock { | ||
execution_receipt, | ||
operator_ids: sp_std::vec![submitter], | ||
}; | ||
BlockTree::<T>::mutate(domain_id, domain_block_number, |er_hashes| { | ||
er_hashes | ||
.try_insert(er_hash) | ||
.map_err(|_| Error::ExceedMaxBlockTreeFork)?; | ||
Ok(()) | ||
})?; | ||
DomainBlocks::<T>::insert(er_hash, domain_block); | ||
|
||
if er_type == ReceiptType::NewHead { | ||
// Update the head receipt number | ||
HeadReceiptNumber::<T>::insert(domain_id, domain_block_number); | ||
|
||
// Prune expired domain block | ||
if let Some(to_prune) = | ||
domain_block_number.checked_sub(&T::BlockTreePruningDepth::get()) | ||
{ | ||
for block in BlockTree::<T>::take(domain_id, to_prune) { | ||
DomainBlocks::<T>::remove(block); | ||
} | ||
// Remove the block's `ExecutionInbox` as the block is pruned and does not need | ||
// to verify its receipt's `extrinsics_root` anymore | ||
ExecutionInbox::<T>::remove(domain_id, to_prune); | ||
} | ||
} | ||
} | ||
ReceiptType::CurrentHead => { | ||
// Add confirmation to the current head receipt | ||
DomainBlocks::<T>::mutate(er_hash, |maybe_domain_block| { | ||
let domain_block = maybe_domain_block.as_mut().expect( | ||
"The domain block of `CurrentHead` receipt is checked to be exist in `execution_receipt_type`; qed" | ||
); | ||
domain_block.operator_ids.push(submitter); | ||
liuchengxu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
} | ||
// Other types of receipt is unexpected for this function | ||
_ => return Err(Error::UnexpectedReceiptType), | ||
NingLin-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
Ok(()) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont understant this. IIUC
head_receipt_number
should be the head of the fork this receipt is part of. But head receipt number you are using may or may not be the sameThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
HeadReceiptNumber
is applied to the whole block tree not any specific fork in the tree, refer #1650 (comment).