出块流程
- 为平行链尽可能平均地分配validator group;
- collator生成一个平行链区块,和该区块的PoV一起组成collation/candidate;
- collator将candidate通过Collator Protocol传递给group内的validator(1 to 1, 非广播);
- group内的validators通过Candidate Backing对candidate进行验证和共识。collation通过共识后状态变更为”backable”;
- 中继链出块人将backable candidate打包上链,此时candidate状态为”pending availability”,此时不会更新parachain的head;
- 在后续的中继链区块中,validators会通过Availability Distribution子系统使得candidate变为available(所有validators的2/3以上持有candidate的PoV);
- 中继链出块人将available的candidate打包上链,此时更新parachain的head,并对candidate中包含的消息(主要是跨链消息)进行处理;
- 类似于finalize,对candidate也有Approval Process的过程,来对candidate进行二次检查,如果有validator报告检查不通过,那么触发所有validators对该candidate进行重新验证。
中继链
启动
- 中继链启动时,启动overseer,包含一些子系统:candidate_backing,candidate_selection,statement_distribution,availability_distribution等,
route_message为子系统进行AllMessages消息中继,发送到指定的子系统。中继链forward_events将中继链主系统的消息传递给overseer。 - 中继链节点与overseer交互由事件触发,主要的事件是块导入,块终结,send_msg。
Event::BlockImported和Event::BlockFinalized会更改active_leaves,send_msg主要用来初始化collation_generation子系统,激活CollatorProtocol,向provisioner请求候选区块。- OverseerHandler{events_tx} 外部发送事件,overseer.events_rx接收外部消息。
- 子系统持有OverseerSubsystemContext 与overseer 交互
- 子系统A,B交互,A发送AllMessages::B,overseer转换为Fromoverseer::Communication{B}发送给B。
请求候选区块
CollatorProtocol::validator_side:
- 接受到来自collator的
Declare,将collator的信息(peer_id, collatorId)保存到State.known_collators。collatorId是collator的公钥。 - 接受到来自collator的
AdvertiseCollation,触发notify_candidate_selection:通知CandidateSelectionSubsystem。 - CandidateSelectionSubsystem
handle_collation:- 检查
para_id是否是该validator负责的,如果不是,forward_invalidity_note->report_collator降低collator的信誉。 get_collation发送CollatorProtocolMessage::FetchCollation,触发fetch_collation,等待回应:request_collation发送CollatorProtocolMessage::RequestCollation(relay_parent, para_id),向known_collators请求collation。- 收到collator发来的protocol_v1::CollatorProtocolMessage::Collation,触发
received_collation:取消仍然正在进行的request_collation(第一次选择了Collation的地方,先到先得,还没准备好发送的collator被排除),唤醒requests_in_progress(如果超时,惩罚collator),回复请求一个元组(candidate_receipt, pov)唤醒get_collation,存储collation。如果是过时的collation,会降低collator的声誉。
second_candidate:发送CandidateBackingMessage::Second(relay_parent,candidate_receipt,pov)给 candidate backing子系统;
- 检查
验证、支持候选区块
- candidate backing子系统
process_msg处理 CandidateBackingMessage::Second。- 检查
para_id是否是self.assignment; - 检查是否second(支持)过其他candidate;
- 检查是否对该candidate已经有issued_statements;
validate_and_second- 检查candidate中的collator是否为schdule计划中的,如果无效,惩罚collator;
- background_validate_and_make_available:将candidate加入
awaiting_validation,启动异步任务validate_and_make_available:- 从params获取到PoV(Second消息中携带了PoV);
request_candidate_validation发送CandidateValidationMessage::ValidateFromChainState,candidate validation子系统spawn_validate_from_chain_statefind_assumed_validation_data检查candidate的PersistedValidationData是否与中继链上一致,这里也检查了parent_head;spawn_validate_exhaustive->validate_candidate_exhaustive,perform_basic_checks基础检查:pov大小是否超过max_pov_size,candidate.descriptor.pov_hash与pov.hash是否一致,check_collator_signature;validate->wasm_executor::validate_candidate->pool.validate_candidate->validate_candidate_custom->ValidationHost::validate_candidate:start_worker通过process::Command::new子命令创建子进程执行run_worker,父子进程通过共享内存进行通信run_worker->validate_candidate_internal- 调用validation_code中的
validate_block(ValidationParams):E::execute_block(block)执行区块,然后读取变更后的存储组装成ValidationResult(数据结构与CandidateCommitments一致)
- 调用validation_code中的
- 检查ValidationResult.head_data.hash() == descriptor.para_head ?
- 将ValidationResult组装成CandidateCommitments,返回验证结果(CandidateCommitments, persisted_validation_data)
runtime_api_request发送RuntimeApiRequest::CheckValidationOutputs,Runtime api子系统调用inclusion模块的check_validation_outputs_for_runtime_api,主要是检查CandidateCommitments是否满足接受标准:数据大小,消息数量。
- 检查commitments_hash是否与ValidationResuld的一致;
make_pov_available:生成纠删码,验证erasure_root。store_available_data:发送AvailabilityStoreMessage::StoreAvailableData(CandidateReceipt.hash, self.ValidatorIndex, n_validators, AvailableData, tx),av store存储可用数据available_data和整个纠删码。- 发送
ValidatedCandidateCommand::Second(candidate, commitments, pov)validate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37// 主要是persisted_validation_data的数据
pub struct ValidationParams {
/// Previous head-data.
pub parent_head: HeadData, // persisted_validation_data.parent_head
/// The collation body.
pub block_data: BlockData, // pov.block_data
/// The current relay-chain block number.
pub relay_chain_height: RelayChainBlockNumber,
/// The relay-chain block's storage root.
pub relay_storage_root: Hash,
/// The MQC head for the DMQ.
///
/// The DMQ MQC head will be used by the validation function to authorize the downward messages
/// passed by the collator.
pub dmq_mqc_head: Hash,
/// The list of MQC heads for the inbound HRMP channels paired with the sender para ids. This
/// vector is sorted ascending by the para id and doesn't contain multiple entries with the same
/// sender.
pub hrmp_mqc_heads: Vec<(Id, Hash)>,
}
pub struct ValidationResult {
/// New head data that should be included in the relay chain state.
pub head_data: HeadData,
/// An update to the validation code that should be scheduled in the relay chain.
pub new_validation_code: Option<ValidationCode>,
/// Upward messages send by the Parachain.
pub upward_messages: Vec<UpwardMessage>,
/// Outbound horizontal messages sent by the parachain.
pub horizontal_messages: Vec<OutboundHrmpMessage<Id>>,
/// Number of downward messages that were processed by the Parachain.
///
/// It is expected that the Parachain processes them from first to last.
pub processed_downward_messages: u32,
/// The mark which specifies the block number up to which all inbound HRMP messages are processed.
pub hrmp_watermark: RelayChainBlockNumber,
}AvailableData
1
2
3
4
5
6pub struct AvailableData {
/// The Proof-of-Validation of the candidate.
pub pov: std::sync::Arc<PoV>,
/// The persisted validation data needed for secondary checks.
pub validation_data: PersistedValidationData,
}
- 检查
对候选区块共识
CandidateBackingJob监听任务,
handle_validated_candidate_command接受命令ValidatedCandidateCommand::Second:将candidate_hash插入self.seconded和self.issued_statements,组装Statement::Seconded(CommittedCandidateReceipt)sign_import_and_distribute_statement对statement共识sign_statement对statement签名import_statementprimitive_statement_to_table转换statement为SignedStatementTable::import_statementimport_candidate:导入Statement::Candidate状态的candidate。digest = CommittedCandidateReceipt.hash()。如果Table.authority_data已经有proposal,验证digest,如果不相符,要报告Misbehavior::MultipleCandidates。Table::candidate_votes插入CandidateData。进行投票ValidityVote::Issued。validity_voterequisite_votes:计算至少需要的票数,group.validators.len()/2 + 1;- 检查本validator是否是group内,如果不是,报告Misbehavior::UnauthorizedStatement;
- 如果已经投票过,报告Misbehavior::ValidityDoubleVote或者Misbehavior::DoubleSign;
- 如果是第一次投票,
insert(vote)。当票数足够时(大于该group的1/2),update_includable_count更新Table::includable_count,记录parachain是否通过。返回Summary。
attested_candidate:仅当can_be_included为true时,才返回AttestedCandidate- 将
summary.candidate(digest)插入CandidateBackingJob的backed集合,table_attested_to_backed将AttestedCandidate转换为BackedCandidate,send_to_provisioner将(relay_parent, CommittedCandidateReceipt) 通过封装在ProvisionableData::BackedCandidate消息中存储到provisioner_subsystem。当can_be_included为false时,不会存储到provisioner_subsystem,中继链就不会打包这个候选区块。 issue_new_misbehaviors报告之前出现的misbehaviors。
distribute_signed_statement:发送StatementDistributionMessage::Share。statement distribution子系统收到后:inform_statement_listeners(collator在wait_to_announce时注册成为listener)circulate_statement_and_dependents(更新后)广播statement:active_head在之前收到OverseerSignal::ActiveLeaves时创建note_statement:做一些简单检查,标记statement的状态,比如如果可以接受并且是未存到active_head的,那么返回NotedStatement::Fresh。- 如果是NotedStatement::Fresh,
circulate_statement查找其他还未拥有该statement的validators,返回peers_needing_dependents。 send_statements_about给peers_needing_dependents发送statements。其他validator
handle_network_update收到NetworkBridgeEvent::PeerMessage后,handle_incoming_message:- 如果是不需要的statement,
report_peer降低peer的信誉; check_statement_signature,签名错误report_peer;- 检查statement是否真的存在于收到的peer data,不存在则
report_peer; - 如果该statement不在该validator的known_candidates中,
send_statements_about发送该candidate的statements给peer。所以group内的validator不一定需要直连,通过p2p网络最终也能收到其他的statement。 - inform_statement_listeners
- note_statement:如果是NotedStatement::Fresh,发送CandidateBackingMessage::Statement。
- CandidateBacking子系统收到后:
- check_statement_signature
maybe_validate_and_importimport_statementkick_off_validation_work:如果已经在issued_statements中则直接退出。检查group_id。如果candidate的collator与required_collator不一致,将其直接插入到issued_statements,然后返回。否则background_validate_and_make_available:此时传入的PoV为None,在validate_and_make_available中会触发request_pov_from_distribution,向所有拥有当前relay_parent的peers广播awaiting pov状态,最后发送ValidatedCandidateCommand::Attest给CandidateBackingJob。- CandidateBackingJob收到Attest后,检查issued_statements,未issue的话插入其中,然后
sign_import_and_distribute_statement。
- 如果是不需要的statement,
- 如果是fresh的,
circulate_statement,但是忽略peers_needing_dependents。
distribute_pov:发送PoVDistributionMessage::DistributePoV。- 子系统pov distribution接受DistributePoV后
handle_distribute:distribute_to_awaiting把pov发给等待该relay_parent的validator(之前有request_pov_from_distribution),发送PoVDistributionMessage::SendPoV,其他validator收到后handle_incoming_pov继续distribute_to_awaiting。
- 子系统pov distribution接受DistributePoV后
CommittedCandidateReceipt
1 | pub struct CommittedCandidateReceipt<H = Hash> { |
CandidateData
1 | pub struct CandidateData<Ctx: Context> { |
Summary
1 | pub struct Summary<Digest, Group> { |
AttestedCandidate
1 | pub struct AttestedCandidate<Group, Candidate, AuthorityId, Signature> { |
BackedCandidate
1 | pub struct BackedCandidate<H = Hash> { |
可用性检查
Availability Distribution子系统:
handle_our_view_change:将处于pending availability队列中(根据core获得,core的状态是根据链上存储计算的)的backed candidates对应的ErasureChunk广播给peers(如果拥有的话)。遍历所有validator获取chunk_index,获取对应的ErasureChunk;process_incoming_peer_message:发送StoreChunk给av store来保存与本validator_index对应的chunk。handle_peer_view_change:将所有活跃信息重新发送给该peer;
BitfieldSigning子系统:每次收到ActiveLeaves(ActiveLeavesUpdate)都会派发一个BitfieldSigningJob,延迟1.5秒后
construct_availability_bitfield:get_availability_cores;get_core_availability:发送AvailabilityStoreMessage::QueryChunkAvailability(candidate, validator_index, tx)向Availability Store查询对于该validator对某个Core是否拥有availability chunk- 遍历所有availability_cores,得到bitfield: Vec
;
validator.sign对bitfield签名;发送BitfieldDistributionMessage::DistributeBitfield(relay_parent, signed_bitfield)。
bitfield distribution收到后,
handle_bitfield_distribution处理signed_bitfield:validator_index从签名中获取validator_index,再从ProtocolState::per_relay_parent::validator_set中得到validatorId;relay_message将bitfield发送ProvisionableData::Bitfield给Provisioner记录到signed_bitfields。
ErasureChunk
1 | pub struct ErasureChunk { |
出块
- 中继链
proposeget_provisioner_data- overseer.
wait_for_activation等待relay_parent为中继链父哈希的叶子在overseer上激活,发送ProvisionerMessage::RequestInherentData请求数据,PROPOSE_TIMEOUT(2.5秒)内需要收到数据;
- overseer.
- 打包进inherent_data。
- Provisioner子系统启动job时,ProvisioningJob::inherent_after设置为Delay(PRE_PROPOSE_TIMEOUT) 2秒;
收到RequestInherentData消息后,等待inherent_after空闲,然后
send_inherent_data:回应RequestInherentData请求request_availability_coresselect_availability_bitfields:此时存储在signed_bitfields中的可能有多个bitfields。选择规则:对每个validator,选择通过可用性检查最多的bitfields;select_candidates:- 转换availability_cores为(ScheduledCore, OccupiedCoreAssumption)
request_persisted_validation_data:向RuntimeApi请求;- 比对para_id和persisted_validation_data_hash;
- 发送CandidateBackingMessage::GetBackedCandidates获取candidates: Vec
; - 检查candidates的顺序是否与selected_candidates一致;
- 返回(bitfields, candidates)。
inclusion_inherent模块中
create_inherent,调用Call::inclusion(signed_bitfields, backed_candidates, parent_header)。- 每个块只能执行一次
inclusion; - 检查parent_header;
process_bitfields:处理bitfields,返回已经处理完置为free的core队列,对应的candidates一定已经是baked:- 读取PendingAvailability存储(
process_candidates中会插入) - 根据signed_bitfields签名的validator_index更新pending_availability中的availability_votes[val_idx]为true;
- AvailabilityBitfields::insert(validator_index, record);
- 检查pending_availability::availability_votes是否大于2/3的validators.len();
enact_candidate:T::RewardValidators::reward_backingT::RewardValidators::reward_bitfieldsschedule_code_upgradeprune_dmqenact_upward_messagesprune_hrmpqueue_outbound_hrmpnote_new_head更新平行链head;
- pending_availability.core插入freed队列;
- 读取PendingAvailability存储(
<scheduler::Module<T>>::clear();<scheduler::Module<T>>::schedule(freed,block_number);process_candidates:处理backed的candidates,PendingAvailability::insert(para_id, CandidatePendingAvailability),返回来自scheduled的Vec<(core_index, bakers)>;scheduler::Module::occupied:backed candidates将process_candidates返回的cores进行占用,然后才能进行Availability Distribution与BitfieldSigning;<ump::Module<T>>::process_pending_upward_messages()。
- 每个块只能执行一次
Approval Check
group内的validator可能作恶,需要二次检查(类似finalized)。随机选取一些validator来再次验证平行链区块,如果验证失败,触发escalation,要求所有的validator来验证这个平行链区块。Approval Process是可以一次最终确认多个平行链区块的。
进入中继链出块的共识流程,直到被GRANDPA确认。