This commit is contained in:
bkchr
2024-08-14 00:59:03 +00:00
parent a62c0f6921
commit f562521003
4 changed files with 130 additions and 94 deletions
+64 -46
View File
@@ -1360,8 +1360,7 @@ sharing if multiple parachains use the same data (e.g. same smart contracts).</p
<li><a href="proposed/0103-introduce-core-index-commitment.html#ergonomics">Ergonomics</a></li> <li><a href="proposed/0103-introduce-core-index-commitment.html#ergonomics">Ergonomics</a></li>
<li><a href="proposed/0103-introduce-core-index-commitment.html#compatibility">Compatibility</a> <li><a href="proposed/0103-introduce-core-index-commitment.html#compatibility">Compatibility</a>
<ul> <ul>
<li><a href="proposed/0103-introduce-core-index-commitment.html#versioning">Versioning</a></li> <li><a href="proposed/0103-introduce-core-index-commitment.html#runtime-1">Runtime</a></li>
<li><a href="proposed/0103-introduce-core-index-commitment.html#runtime">Runtime</a></li>
<li><a href="proposed/0103-introduce-core-index-commitment.html#validators">Validators</a></li> <li><a href="proposed/0103-introduce-core-index-commitment.html#validators">Validators</a></li>
<li><a href="proposed/0103-introduce-core-index-commitment.html#parachains">Parachains</a></li> <li><a href="proposed/0103-introduce-core-index-commitment.html#parachains">Parachains</a></li>
<li><a href="proposed/0103-introduce-core-index-commitment.html#tooling">Tooling</a></li> <li><a href="proposed/0103-introduce-core-index-commitment.html#tooling">Tooling</a></li>
@@ -1381,11 +1380,10 @@ sharing if multiple parachains use the same data (e.g. same smart contracts).</p
</tbody></table> </tbody></table>
</div> </div>
<h2 id="summary-5"><a class="header" href="#summary-5">Summary</a></h2> <h2 id="summary-5"><a class="header" href="#summary-5">Summary</a></h2>
<p>The only requirement for collator nodes is to provide valid parachain blocks to the validators of a backing group and by definition the collator set is trustless. However, in the case of elastic scaling, for security reason, collators must be trusted - non-malicious. <code>CoreIndex</code> commitments are required to remove this limitation. Additionally we are introducing a <code>SessionIndex</code> field in the <code>CandidateReceipt</code> to make dispute resolution more secure and robust. </p> <p>Elastic scaling is not resiliet against griefing attacks without a way for a PoV (Proof of Validity) to commit to the particular core index it was intedened for. This RFC proposes a way to include core index information in the candidate commitments and the <code>CandidateDescriptor</code> data strcuture in a backwards compatible way. Additionally it proposes the addition of a <code>SessionIndex</code> field in the <code>CandidateDescriptor</code> to make dispute resolution more secure and robust. </p>
<h2 id="motivation-5"><a class="header" href="#motivation-5">Motivation</a></h2> <h2 id="motivation-5"><a class="header" href="#motivation-5">Motivation</a></h2>
<p>At present time misbehaving collator nodes, or anyone who has acquired a valid collation can prevent a parachain from effecitvely using elastic scaling by providing the same collation to all backing groups assigned to the parachain. This happens before the next parachain block is authored and will prevent the chain of candidates to be formed, reducing the throughput of the parachain to a single core.</p> <p>At present time misbehaving collator nodes, or anyone who has acquired a valid collation can prevent a parachain from effecitvely using elastic scaling by providing the same collation to all backing groups assigned to the parachain. This happens before the next parachain block is authored and will prevent the chain of candidates to be formed, reducing the throughput of the parachain to a single core.</p>
<p>This RFC solves the problem by enabling a parachain to provide a core index commitment as part of it's PVF execution output and in the associated candidate receipt data structure. </p> <p>The session index of candidates is important for the disputes protocol as it is used to lookup validator keys and check dispute vote signatures. By adding a <code>SessionIndex</code> in the <code>CandidateDescriptor</code>, validators no longer have to trust the <code>Sessionindex</code> provided by the validator raising a dispute. It can happen that the dispute concerns a relay chain block not yet imported by a validator. In this case validators can safely assume the session index refers to the session the candidate has appeared in, otherwise the chain would have rejected candidate.</p>
<p>Once this RFC is implemented the validity of a parachain block depends on the core it is being executed on.</p>
<h2 id="stakeholders-5"><a class="header" href="#stakeholders-5">Stakeholders</a></h2> <h2 id="stakeholders-5"><a class="header" href="#stakeholders-5">Stakeholders</a></h2>
<ul> <ul>
<li>Polkadot core developers.</li> <li>Polkadot core developers.</li>
@@ -1398,7 +1396,7 @@ sharing if multiple parachains use the same data (e.g. same smart contracts).</p
<h3 id="reclaiming-unused-space-in-the-descriptor"><a class="header" href="#reclaiming-unused-space-in-the-descriptor">Reclaiming unused space in the descriptor</a></h3> <h3 id="reclaiming-unused-space-in-the-descriptor"><a class="header" href="#reclaiming-unused-space-in-the-descriptor">Reclaiming unused space in the descriptor</a></h3>
<p>The <code>CandidateDescriptor</code> currently includes <code>collator</code> and <code>signature</code> fields. The collator includes a signature on the following descriptor fields: parachain id, relay parent, validation data hash, validation code hash and the PoV hash.</p> <p>The <code>CandidateDescriptor</code> currently includes <code>collator</code> and <code>signature</code> fields. The collator includes a signature on the following descriptor fields: parachain id, relay parent, validation data hash, validation code hash and the PoV hash.</p>
<p>However, in practice, having a collator signature in the receipt on the relay chain does not provide any benefits as there is no mechanism to punish or reward collators that have provided bad parachain blocks.</p> <p>However, in practice, having a collator signature in the receipt on the relay chain does not provide any benefits as there is no mechanism to punish or reward collators that have provided bad parachain blocks.</p>
<p>This proposal aims to remove the collator signature and all the logic that checks the collator signatures of candidate receipts. We use the first 6 reclaimed bytes to represent the core and session index, and fill the rest with zeroes. So, there is no change in the layout and length of the receipt. The new primitive is binary compatible with the old one.</p> <p>This proposal aims to remove the collator signature and all the logic that checks the collator signatures of candidate receipts. We use the first 7 reclaimed bytes to represent version, the core and session index, and fill the rest with zeroes. So, there is no change in the layout and length of the receipt. The new primitive is binary compatible with the old one.</p>
<h3 id="backwards-compatibility"><a class="header" href="#backwards-compatibility">Backwards compatibility</a></h3> <h3 id="backwards-compatibility"><a class="header" href="#backwards-compatibility">Backwards compatibility</a></h3>
<p>There are two flavors of candidate receipts which are used in network protocols, runtime and node implementation:</p> <p>There are two flavors of candidate receipts which are used in network protocols, runtime and node implementation:</p>
<ul> <ul>
@@ -1406,22 +1404,27 @@ sharing if multiple parachains use the same data (e.g. same smart contracts).</p
<li><code>CandidateReceipt</code> which includes the <code>CanidateDescriptor</code> and just a hash of the commitments</li> <li><code>CandidateReceipt</code> which includes the <code>CanidateDescriptor</code> and just a hash of the commitments</li>
</ul> </ul>
<p>We want to support both the old and new versions in the runtime and node. The implementation must be able to detect the version of a given candidate receipt.</p> <p>We want to support both the old and new versions in the runtime and node. The implementation must be able to detect the version of a given candidate receipt.</p>
<p><code>CandidateDescriptor</code> is at version 2 if:</p> <p><code>CandidateDescriptor</code> is a valid version 2 descriptor, if:</p>
<ul> <ul>
<li>version field is 0</li>
<li>the reserved fields are zeroed</li> <li>the reserved fields are zeroed</li>
<li>the session index matches the session index of the relay parent</li> <li>the session index matches the session index of the relay parent</li>
<li>the UMP queue contains a core index commitment and it the one in the descriptor.</li> <li>the UMP queue contains a core index commitment and it matches the one in the descriptor.</li>
</ul> </ul>
<h3 id="ump-transport"><a class="header" href="#ump-transport">UMP transport</a></h3> <h3 id="ump-transport"><a class="header" href="#ump-transport">UMP transport</a></h3>
<p><a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L652">CandidateCommitments</a> remains unchanged as we will store scale encoded <code>UMPSignal</code> messages directly in the parachain UMP queue by outputing them in the <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L682">upward_messages</a>. </p> <p><a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L652">CandidateCommitments</a> remains unchanged as we will store scale encoded <code>UMPSignal</code> messages directly in the parachain UMP queue by outputing them in the <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L682">upward_messages</a>. </p>
<p>The UMP queue layout is changed to allow the relay chain to receive both the XCM messages and <code>UMPSignal</code> messages. An empty message (empty <code>Vec&lt;u8&gt;</code>) is used to mark the end XCM messages and the start of <code>UMPSignal</code> messages.</p> <p>The UMP queue layout is changed to allow the relay chain to receive both the XCM messages and <code>UMPSignal</code> messages. An empty message (empty <code>Vec&lt;u8&gt;</code>) is used to mark the end XCM messages and the start of <code>UMPSignal</code> messages.</p>
<p>This way of representing the new messages has been choose over introducing an enum wrapper to minimize breaking changes of XCM message decoding in tools like Subscan for example. </p> <p>This way of representing the new messages has been chosen over introducing an enum wrapper to minimize breaking changes of XCM message decoding in tools like Subscan for example. </p>
<p>Example: </p> <p>Example: </p>
<pre><code>[ XCM message1, XCM message2, ..., EMPTY message, UMPSignal::CoreSelector ] <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</code></pre> </span><span class="boring">fn main() {
</span>[ XCM message1, XCM message2, ..., EMPTY message, UMPSignal::CoreSelector ]
<span class="boring">}</span></code></pre></pre>
<h4 id="umpsignal-messages"><a class="header" href="#umpsignal-messages"><code>UMPSignal</code> messages</a></h4> <h4 id="umpsignal-messages"><a class="header" href="#umpsignal-messages"><code>UMPSignal</code> messages</a></h4>
<pre><code>/// A strictly increasing sequence number, tipically this would be the parachain block number. <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
pub struct CoreSelector(pub BlockNumber); </span><span class="boring">fn main() {
</span>/// An `u8` wrap around sequence number. Typically this would be the least significant byte of the parachain block number.
pub struct CoreSelector(pub u8);
/// An offset in the relay chain claim queue. /// An offset in the relay chain claim queue.
pub struct ClaimQueueOffset(pub u8); pub struct ClaimQueueOffset(pub u8);
@@ -1436,34 +1439,42 @@ pub enum UMPSignal {
/// ///
SelectCore(CoreSelector, ClaimQueueOffset), SelectCore(CoreSelector, ClaimQueueOffset),
} }
</code></pre> <span class="boring">}</span></code></pre></pre>
<p>As we dont want to have a claim queue snapshot in the parachain runtime, we need to set <code>ClaimQueueOffset</code> <p>As we dont want to have a claim queue snapshot in the parachain runtime, we need to set <code>ClaimQueueOffset</code>
statically to some sane value. Parachains should prefer to have a static value that makes sense for their usecase which can be changed by governance at some future point. Changing the value dynamically can be a friction point. It will work out fine to decrease the value to build more into the present. But if the value is increased to build more into the future, a relay chain block will be skipped.</p> statically to some sane value. Parachains should prefer to have a static value that makes sense for their usecase which can be changed by governance at some future point. Changing the value dynamically can be a friction point. It will work out fine to decrease the value to build more into the present. But if the value is increased to build more into the future, a relay chain block will be skipped.</p>
<p>Considering <code>para_assigned_cores</code> is a sorted vec of core indices assigned to a parachain at the <p>Considering <code>para_assigned_cores</code> is a sorted vec of core indices assigned to a parachain at the
specified claim queue offset, validators will determine the committed core index like this:</p> specified claim queue offset, validators will determine the committed core index like this:</p>
<pre><code>let assigned_core_index = core_selector % para_assigned_cores.len(); <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let assigned_core_index = core_selector % para_assigned_cores.len();
let committed_core_index = para_assigned_cores[assigned_core_index]; let committed_core_index = para_assigned_cores[assigned_core_index];
</code></pre> <span class="boring">}</span></code></pre></pre>
<h3 id="polkadot-primitive-changes"><a class="header" href="#polkadot-primitive-changes">Polkadot Primitive changes</a></h3> <h3 id="polkadot-primitive-changes"><a class="header" href="#polkadot-primitive-changes">Polkadot Primitive changes</a></h3>
<h4 id="new-candidatedescriptor"><a class="header" href="#new-candidatedescriptor">New <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L482">CandidateDescriptor</a></a></h4> <h4 id="new-candidatedescriptor"><a class="header" href="#new-candidatedescriptor">New <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L482">CandidateDescriptor</a></a></h4>
<ul> <ul>
<li>reclaim 32 bytes from <code>collator: CollatorId</code> and 64 bytes from <code>signature: CollatorSignature</code> and rename to <code>reserved1</code> and <code>reserved2</code> fields.</li> <li>reclaim 32 bytes from <code>collator: CollatorId</code> and 64 bytes from <code>signature: CollatorSignature</code> and rename to <code>reserved1</code> and <code>reserved2</code> fields.</li>
<li>take 1 bytes from <code>reserved1</code> for a new <code>version: u8</code> field.</li>
<li>take 2 bytes from <code>reserved1</code> for a new <code>core_index: u16</code> field.</li> <li>take 2 bytes from <code>reserved1</code> for a new <code>core_index: u16</code> field.</li>
<li>take 4 bytes from <code>reserved2</code> for a new <code>session_index: u32</code> field.</li> <li>take 4 bytes from <code>reserved1</code> for a new <code>session_index: u32</code> field.</li>
<li>the <code>reserved1</code> and <code>reserved2</code> fields are zeroed</li> <li>the remaining <code>reserved1</code> and <code>reserved2</code> fields are zeroed</li>
</ul> </ul>
<p>Thew new primitive will look like this:</p> <p>Thew new primitive will look like this:</p>
<pre><code>pub struct CandidateDescriptorV2&lt;H = Hash&gt; { <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct CandidateDescriptorV2&lt;H = Hash&gt; {
/// The ID of the para this is a candidate for. /// The ID of the para this is a candidate for.
para_id: Id, para_id: ParaId,
/// The hash of the relay-chain block this is executed in the context of. /// The hash of the relay-chain block this is executed in the context of.
relay_parent: H, relay_parent: H,
/// Version field. The raw value here is not exposed, instead it is used
/// to determine the `CandidateDescriptorVersion`, see `fn version()`
version: InternalVersion,
/// The core index where the candidate is backed. /// The core index where the candidate is backed.
core_index: CoreIndex, core_index: u16,
/// The session index in which the candidate is backed. /// The session index of the candidate relay parent.
session_index: SessionIndex, session_index: SessionIndex,
/// Reserved bytes. /// Reserved bytes.
reserved1: [u8; 26], reserved25b: [u8; 25],
/// The blake2-256 hash of the persisted validation data. This is extra data derived from /// The blake2-256 hash of the persisted validation data. This is extra data derived from
/// relay-chain state which may vary based on bitfields included before the candidate. /// relay-chain state which may vary based on bitfields included before the candidate.
/// Thus it cannot be derived entirely from the relay-parent. /// Thus it cannot be derived entirely from the relay-parent.
@@ -1473,35 +1484,39 @@ let committed_core_index = para_assigned_cores[assigned_core_index];
/// The root of a block's erasure encoding Merkle tree. /// The root of a block's erasure encoding Merkle tree.
erasure_root: Hash, erasure_root: Hash,
/// Reserved bytes. /// Reserved bytes.
reserved2: [u8; 64], reserved64b: [u8; 64],
/// Hash of the para header that is being generated by this candidate. /// Hash of the para header that is being generated by this candidate.
para_head: Hash, para_head: Hash,
/// The blake2-256 hash of the validation code bytes. /// The blake2-256 hash of the validation code bytes.
validation_code_hash: ValidationCodeHash, validation_code_hash: ValidationCodeHash,
} }
</code></pre> <span class="boring">}</span></code></pre></pre>
<p>In future format versions, parts of the <code>reserved1</code> and <code>reserved2</code> bytes can be used to include additional information in the descriptor.</p> <p>In future format versions, parts of the <code>reserved1</code> and <code>reserved2</code> bytes can be used to include additional information in the descriptor.</p>
<h4 id="versioned-candidatereceipt-and-committedcandidatereceipt-primitives"><a class="header" href="#versioned-candidatereceipt-and-committedcandidatereceipt-primitives">Versioned <code>CandidateReceipt</code> and <code>CommittedCandidateReceipt</code> primitives:</a></h4> <h4 id="candidate-descriptor-api"><a class="header" href="#candidate-descriptor-api">Candidate descriptor API</a></h4>
<p>We want to decouple the actual representation of the <code>CandidateReceipt</code> from the higher level code. This should make it easier to implement future format versions of this primitive. To hide the logic of versioning the descriptor fields will be private and accessor methods are provided.</p> <p>We want to decouple the actual representation of the <code>CandidateDescriptor</code> from the higher level code. This should make it easier to implement future format versions of this primitive. To hide the logic of versioning the descriptor fields will be private and getter methods are provided for all the fields. </p>
<pre><code>pub enum VersionedCandidateReceipt { <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
V1(CandidateReceipt), </span><span class="boring">fn main() {
V2(CandidateReceiptV2), </span>impl&lt;H&gt; CandidateDescriptorV2&lt;H&gt; {
}
impl VersionedCandidateReceipt { /// Returns the collator id in the descriptor. Returns `None` if descriptor is at verision 2.
/// Returns the core index the candidate has commited to. pub fn collator(&amp;self) -&gt; Option&lt;CollatorId&gt;;
/// Returns `None` if the candidate receipt is the old version (v1).
fn core_index() -&gt; Option&lt;CoreIndex&gt;;
/// Returns the session index of the candidate relay parent. /// Returns the collator signature in the descriptor. Returns `None` if descriptor is at verision 2.
fn session_index() -&gt; Option&lt;CoreIndex&gt;; pub fn signature(&amp;self) -&gt; Option&lt;CollatorSignature&gt;;
/// Returns the core index of the descriptor. Returns `None` if the descriptor is at version 1.
pub fn core_index(&amp;self) -&gt; Option&lt;CoreIndex&gt;;
/// Returns the session index of the descriptor. Returns `None` if the descriptor is at version 1.
pub fn session_index(&amp;self) -&gt; Option&lt;SessionIndex&gt;;
/// ... /// ...
} }
</code></pre> <span class="boring">}</span></code></pre></pre>
<p>A manual decode <code>Decode</code> implementation is required to account for version detection and constructing the appropriate variant.</p> <p>A manual decode <code>Decode</code> implementation is required to account for version detection and constructing the appropriate variant.</p>
<h3 id="parachain-block-validation"><a class="header" href="#parachain-block-validation">Parachain block validation</a></h3> <h3 id="parachain-block-validation"><a class="header" href="#parachain-block-validation">Parachain block validation</a></h3>
<h4 id="node"><a class="header" href="#node">Node</a></h4>
<p>Backers will make use of the core index information to validate the blocks during backing and reject blocks if:</p> <p>Backers will make use of the core index information to validate the blocks during backing and reject blocks if:</p>
<ul> <ul>
<li>the <code>core_index</code> in descriptor does not match the one determined by the <code>UMPSignal::SelectCore</code> message</li> <li>the <code>core_index</code> in descriptor does not match the one determined by the <code>UMPSignal::SelectCore</code> message</li>
@@ -1509,6 +1524,8 @@ impl VersionedCandidateReceipt {
<li>the <code>session_index</code> is not equal to the session of the <code>relay_parent</code> in the descriptor</li> <li>the <code>session_index</code> is not equal to the session of the <code>relay_parent</code> in the descriptor</li>
</ul> </ul>
<p>If core index (and session index) information is not available (backers got an old candidate receipt), there will be no changes compared to current behaviour.</p> <p>If core index (and session index) information is not available (backers got an old candidate receipt), there will be no changes compared to current behaviour.</p>
<h4 id="runtime"><a class="header" href="#runtime">Runtime</a></h4>
<p>The runtime will also perform the above checks and reject invalid candidates.</p>
<h2 id="drawbacks-5"><a class="header" href="#drawbacks-5">Drawbacks</a></h2> <h2 id="drawbacks-5"><a class="header" href="#drawbacks-5">Drawbacks</a></h2>
<p>The only drawback is that further additions to the descriptor are limited to the amount of remaining unused space.</p> <p>The only drawback is that further additions to the descriptor are limited to the amount of remaining unused space.</p>
<h2 id="testing-security-and-privacy-5"><a class="header" href="#testing-security-and-privacy-5">Testing, Security, and Privacy</a></h2> <h2 id="testing-security-and-privacy-5"><a class="header" href="#testing-security-and-privacy-5">Testing, Security, and Privacy</a></h2>
@@ -1518,22 +1535,22 @@ impl VersionedCandidateReceipt {
<h2 id="performance-4"><a class="header" href="#performance-4">Performance</a></h2> <h2 id="performance-4"><a class="header" href="#performance-4">Performance</a></h2>
<p>The expectation is that performance impact is negligible for sending and processing the UMP message has negligible performance impact in the runtime as well as on the node side.</p> <p>The expectation is that performance impact is negligible for sending and processing the UMP message has negligible performance impact in the runtime as well as on the node side.</p>
<h2 id="ergonomics-4"><a class="header" href="#ergonomics-4">Ergonomics</a></h2> <h2 id="ergonomics-4"><a class="header" href="#ergonomics-4">Ergonomics</a></h2>
<p>Parachain that use elastic scaling must send the separator empty message followed by the <code>UMPSignal::SelectCore</code> only after sending all of the UMP XCM messages.</p> <p>It is mandatory for elastic parachains to switch to the new receipt format. It is optional but desired that all parachains
switch to the new receipts for providing the session index for disputes.</p>
<p>Once this RFC is implemented the parachain runtime and node must not require any manual changes to use it, except if the parachain wants to change the <code>ClaimQueueOffset</code> that is used to determine the core index. </p>
<h2 id="compatibility-4"><a class="header" href="#compatibility-4">Compatibility</a></h2> <h2 id="compatibility-4"><a class="header" href="#compatibility-4">Compatibility</a></h2>
<h3 id="versioning"><a class="header" href="#versioning">Versioning</a></h3> <p>The proposed changes are backwards compatible in general, but additional care must be taken by waiting for enough validators to upgrade before the validators and runtime start accepting the new candidate receipts.</p>
<p>At this point there is a simple way to determine the version of the receipt, by testing for zeroed reserved bytes in the <h3 id="runtime-1"><a class="header" href="#runtime-1">Runtime</a></h3>
descriptor. Supporting future changes will require a <code>u8</code> version field to be introduced in the reserved space. We consider the current version to be 0 and the version check implicitly done when checking for reserved space to be zeroed.</p>
<h3 id="runtime"><a class="header" href="#runtime">Runtime</a></h3>
<p>The first step is to remove collator signature checking logic in the runtime, but keep the node side collator signature <p>The first step is to remove collator signature checking logic in the runtime, but keep the node side collator signature
checks. </p> checks. </p>
<p>The runtime must be upgraded to the new primitives before any collator or node are allowed to use the new candidate receipts format. </p> <p>The runtime must be upgraded to the new primitives before any collator or node are allowed to use the new candidate receipts format. </p>
<h3 id="validators"><a class="header" href="#validators">Validators</a></h3> <h3 id="validators"><a class="header" href="#validators">Validators</a></h3>
<p>To ensure a smooth launch, a new node feature is required. <p>To ensure a smooth launch, a new node feature is required.
The feature acts as a signal for supporting the new candidate receipts on the node side and can only be safely enabled if at least 2/3 of the validators are upgraded.</p> The feature acts as a signal for supporting the new candidate receipts on the node side and can only be safely enabled if at least 2/3 of the validators are upgraded.</p>
<p>Once enabled, the validators will skip checking the collator signature when processing the candidate receipts and verify the <code>CoreIndex</code> and <code>SessionIndex</code> fields if present in the receipit.</p> <p>Once enabled, the validators will skip checking the collator signature when processing the candidate receipts and verify the <code>CoreIndex</code> and <code>SessionIndex</code> fields if present in the receipt.</p>
<p>No new implementation of networking protocol versions for collation and validation are required.</p> <p>No new implementation of networking protocol versions for collation and validation are required.</p>
<h3 id="parachains"><a class="header" href="#parachains">Parachains</a></h3> <h3 id="parachains"><a class="header" href="#parachains">Parachains</a></h3>
<p><code>CoreIndex</code> commitments are needed only by parachains using elastic scaling. Just upgrading the collator node and runtime should be sufficient and possible without manual changes.</p> <p>The implementation of this RFC will supersede the Elastic MVP feature that relies on injecting a core index in the <code>validator_indices</code> fields of the <code>BackedCandidate</code> primitive. Elastic parachains must upgrade to use the new receipt format.</p>
<h3 id="tooling"><a class="header" href="#tooling">Tooling</a></h3> <h3 id="tooling"><a class="header" href="#tooling">Tooling</a></h3>
<p>Any tooling that decodes UMP XCM messages needs an update to support or ignore the new UMP messages, but they should be fine to decode the regular XCM messages that come before the separator.</p> <p>Any tooling that decodes UMP XCM messages needs an update to support or ignore the new UMP messages, but they should be fine to decode the regular XCM messages that come before the separator.</p>
<h2 id="prior-art-and-references-5"><a class="header" href="#prior-art-and-references-5">Prior Art and References</a></h2> <h2 id="prior-art-and-references-5"><a class="header" href="#prior-art-and-references-5">Prior Art and References</a></h2>
@@ -1542,7 +1559,8 @@ The feature acts as a signal for supporting the new candidate receipts on the no
<p>N/A</p> <p>N/A</p>
<h2 id="future-directions-and-related-material-5"><a class="header" href="#future-directions-and-related-material-5">Future Directions and Related Material</a></h2> <h2 id="future-directions-and-related-material-5"><a class="header" href="#future-directions-and-related-material-5">Future Directions and Related Material</a></h2>
<p>The implementation is extensible and future proof to some extent. With minimal or no breaking changes, additional fields can be added in the candidate descriptor until the reserved space is exhausted</p> <p>The implementation is extensible and future proof to some extent. With minimal or no breaking changes, additional fields can be added in the candidate descriptor until the reserved space is exhausted</p>
<p>Once the reserved space is exhausted, versioning will be implemented. The candidate receipt format will be versioned. Versioning should also be implemented for the validation function, inputs and outputs (<code>CandidateCommitments</code>).</p> <p>At this point there is a simple way to determine the version of the receipt, by testing for zeroed reserved bytes in the
descriptor. Future versions of the receipt can be implemented and identified by using the <code>version</code> field of the descirptor introduced in this RFC.</p>
<div style="break-before: page; page-break-before: always;"></div><p><a href="https://github.com/polkadot-fellows/RFCs/pull/105">(source)</a></p> <div style="break-before: page; page-break-before: always;"></div><p><a href="https://github.com/polkadot-fellows/RFCs/pull/105">(source)</a></p>
<p><strong>Table of Contents</strong></p> <p><strong>Table of Contents</strong></p>
<ul> <ul>
@@ -197,8 +197,7 @@
<li><a href="#ergonomics">Ergonomics</a></li> <li><a href="#ergonomics">Ergonomics</a></li>
<li><a href="#compatibility">Compatibility</a> <li><a href="#compatibility">Compatibility</a>
<ul> <ul>
<li><a href="#versioning">Versioning</a></li> <li><a href="#runtime-1">Runtime</a></li>
<li><a href="#runtime">Runtime</a></li>
<li><a href="#validators">Validators</a></li> <li><a href="#validators">Validators</a></li>
<li><a href="#parachains">Parachains</a></li> <li><a href="#parachains">Parachains</a></li>
<li><a href="#tooling">Tooling</a></li> <li><a href="#tooling">Tooling</a></li>
@@ -218,11 +217,10 @@
</tbody></table> </tbody></table>
</div> </div>
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2> <h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
<p>The only requirement for collator nodes is to provide valid parachain blocks to the validators of a backing group and by definition the collator set is trustless. However, in the case of elastic scaling, for security reason, collators must be trusted - non-malicious. <code>CoreIndex</code> commitments are required to remove this limitation. Additionally we are introducing a <code>SessionIndex</code> field in the <code>CandidateReceipt</code> to make dispute resolution more secure and robust. </p> <p>Elastic scaling is not resiliet against griefing attacks without a way for a PoV (Proof of Validity) to commit to the particular core index it was intedened for. This RFC proposes a way to include core index information in the candidate commitments and the <code>CandidateDescriptor</code> data strcuture in a backwards compatible way. Additionally it proposes the addition of a <code>SessionIndex</code> field in the <code>CandidateDescriptor</code> to make dispute resolution more secure and robust. </p>
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2> <h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
<p>At present time misbehaving collator nodes, or anyone who has acquired a valid collation can prevent a parachain from effecitvely using elastic scaling by providing the same collation to all backing groups assigned to the parachain. This happens before the next parachain block is authored and will prevent the chain of candidates to be formed, reducing the throughput of the parachain to a single core.</p> <p>At present time misbehaving collator nodes, or anyone who has acquired a valid collation can prevent a parachain from effecitvely using elastic scaling by providing the same collation to all backing groups assigned to the parachain. This happens before the next parachain block is authored and will prevent the chain of candidates to be formed, reducing the throughput of the parachain to a single core.</p>
<p>This RFC solves the problem by enabling a parachain to provide a core index commitment as part of it's PVF execution output and in the associated candidate receipt data structure. </p> <p>The session index of candidates is important for the disputes protocol as it is used to lookup validator keys and check dispute vote signatures. By adding a <code>SessionIndex</code> in the <code>CandidateDescriptor</code>, validators no longer have to trust the <code>Sessionindex</code> provided by the validator raising a dispute. It can happen that the dispute concerns a relay chain block not yet imported by a validator. In this case validators can safely assume the session index refers to the session the candidate has appeared in, otherwise the chain would have rejected candidate.</p>
<p>Once this RFC is implemented the validity of a parachain block depends on the core it is being executed on.</p>
<h2 id="stakeholders"><a class="header" href="#stakeholders">Stakeholders</a></h2> <h2 id="stakeholders"><a class="header" href="#stakeholders">Stakeholders</a></h2>
<ul> <ul>
<li>Polkadot core developers.</li> <li>Polkadot core developers.</li>
@@ -235,7 +233,7 @@
<h3 id="reclaiming-unused-space-in-the-descriptor"><a class="header" href="#reclaiming-unused-space-in-the-descriptor">Reclaiming unused space in the descriptor</a></h3> <h3 id="reclaiming-unused-space-in-the-descriptor"><a class="header" href="#reclaiming-unused-space-in-the-descriptor">Reclaiming unused space in the descriptor</a></h3>
<p>The <code>CandidateDescriptor</code> currently includes <code>collator</code> and <code>signature</code> fields. The collator includes a signature on the following descriptor fields: parachain id, relay parent, validation data hash, validation code hash and the PoV hash.</p> <p>The <code>CandidateDescriptor</code> currently includes <code>collator</code> and <code>signature</code> fields. The collator includes a signature on the following descriptor fields: parachain id, relay parent, validation data hash, validation code hash and the PoV hash.</p>
<p>However, in practice, having a collator signature in the receipt on the relay chain does not provide any benefits as there is no mechanism to punish or reward collators that have provided bad parachain blocks.</p> <p>However, in practice, having a collator signature in the receipt on the relay chain does not provide any benefits as there is no mechanism to punish or reward collators that have provided bad parachain blocks.</p>
<p>This proposal aims to remove the collator signature and all the logic that checks the collator signatures of candidate receipts. We use the first 6 reclaimed bytes to represent the core and session index, and fill the rest with zeroes. So, there is no change in the layout and length of the receipt. The new primitive is binary compatible with the old one.</p> <p>This proposal aims to remove the collator signature and all the logic that checks the collator signatures of candidate receipts. We use the first 7 reclaimed bytes to represent version, the core and session index, and fill the rest with zeroes. So, there is no change in the layout and length of the receipt. The new primitive is binary compatible with the old one.</p>
<h3 id="backwards-compatibility"><a class="header" href="#backwards-compatibility">Backwards compatibility</a></h3> <h3 id="backwards-compatibility"><a class="header" href="#backwards-compatibility">Backwards compatibility</a></h3>
<p>There are two flavors of candidate receipts which are used in network protocols, runtime and node implementation:</p> <p>There are two flavors of candidate receipts which are used in network protocols, runtime and node implementation:</p>
<ul> <ul>
@@ -243,22 +241,27 @@
<li><code>CandidateReceipt</code> which includes the <code>CanidateDescriptor</code> and just a hash of the commitments</li> <li><code>CandidateReceipt</code> which includes the <code>CanidateDescriptor</code> and just a hash of the commitments</li>
</ul> </ul>
<p>We want to support both the old and new versions in the runtime and node. The implementation must be able to detect the version of a given candidate receipt.</p> <p>We want to support both the old and new versions in the runtime and node. The implementation must be able to detect the version of a given candidate receipt.</p>
<p><code>CandidateDescriptor</code> is at version 2 if:</p> <p><code>CandidateDescriptor</code> is a valid version 2 descriptor, if:</p>
<ul> <ul>
<li>version field is 0</li>
<li>the reserved fields are zeroed</li> <li>the reserved fields are zeroed</li>
<li>the session index matches the session index of the relay parent</li> <li>the session index matches the session index of the relay parent</li>
<li>the UMP queue contains a core index commitment and it the one in the descriptor.</li> <li>the UMP queue contains a core index commitment and it matches the one in the descriptor.</li>
</ul> </ul>
<h3 id="ump-transport"><a class="header" href="#ump-transport">UMP transport</a></h3> <h3 id="ump-transport"><a class="header" href="#ump-transport">UMP transport</a></h3>
<p><a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L652">CandidateCommitments</a> remains unchanged as we will store scale encoded <code>UMPSignal</code> messages directly in the parachain UMP queue by outputing them in the <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L682">upward_messages</a>. </p> <p><a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L652">CandidateCommitments</a> remains unchanged as we will store scale encoded <code>UMPSignal</code> messages directly in the parachain UMP queue by outputing them in the <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L682">upward_messages</a>. </p>
<p>The UMP queue layout is changed to allow the relay chain to receive both the XCM messages and <code>UMPSignal</code> messages. An empty message (empty <code>Vec&lt;u8&gt;</code>) is used to mark the end XCM messages and the start of <code>UMPSignal</code> messages.</p> <p>The UMP queue layout is changed to allow the relay chain to receive both the XCM messages and <code>UMPSignal</code> messages. An empty message (empty <code>Vec&lt;u8&gt;</code>) is used to mark the end XCM messages and the start of <code>UMPSignal</code> messages.</p>
<p>This way of representing the new messages has been choose over introducing an enum wrapper to minimize breaking changes of XCM message decoding in tools like Subscan for example. </p> <p>This way of representing the new messages has been chosen over introducing an enum wrapper to minimize breaking changes of XCM message decoding in tools like Subscan for example. </p>
<p>Example: </p> <p>Example: </p>
<pre><code>[ XCM message1, XCM message2, ..., EMPTY message, UMPSignal::CoreSelector ] <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</code></pre> </span><span class="boring">fn main() {
</span>[ XCM message1, XCM message2, ..., EMPTY message, UMPSignal::CoreSelector ]
<span class="boring">}</span></code></pre></pre>
<h4 id="umpsignal-messages"><a class="header" href="#umpsignal-messages"><code>UMPSignal</code> messages</a></h4> <h4 id="umpsignal-messages"><a class="header" href="#umpsignal-messages"><code>UMPSignal</code> messages</a></h4>
<pre><code>/// A strictly increasing sequence number, tipically this would be the parachain block number. <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
pub struct CoreSelector(pub BlockNumber); </span><span class="boring">fn main() {
</span>/// An `u8` wrap around sequence number. Typically this would be the least significant byte of the parachain block number.
pub struct CoreSelector(pub u8);
/// An offset in the relay chain claim queue. /// An offset in the relay chain claim queue.
pub struct ClaimQueueOffset(pub u8); pub struct ClaimQueueOffset(pub u8);
@@ -273,34 +276,42 @@ pub enum UMPSignal {
/// ///
SelectCore(CoreSelector, ClaimQueueOffset), SelectCore(CoreSelector, ClaimQueueOffset),
} }
</code></pre> <span class="boring">}</span></code></pre></pre>
<p>As we dont want to have a claim queue snapshot in the parachain runtime, we need to set <code>ClaimQueueOffset</code> <p>As we dont want to have a claim queue snapshot in the parachain runtime, we need to set <code>ClaimQueueOffset</code>
statically to some sane value. Parachains should prefer to have a static value that makes sense for their usecase which can be changed by governance at some future point. Changing the value dynamically can be a friction point. It will work out fine to decrease the value to build more into the present. But if the value is increased to build more into the future, a relay chain block will be skipped.</p> statically to some sane value. Parachains should prefer to have a static value that makes sense for their usecase which can be changed by governance at some future point. Changing the value dynamically can be a friction point. It will work out fine to decrease the value to build more into the present. But if the value is increased to build more into the future, a relay chain block will be skipped.</p>
<p>Considering <code>para_assigned_cores</code> is a sorted vec of core indices assigned to a parachain at the <p>Considering <code>para_assigned_cores</code> is a sorted vec of core indices assigned to a parachain at the
specified claim queue offset, validators will determine the committed core index like this:</p> specified claim queue offset, validators will determine the committed core index like this:</p>
<pre><code>let assigned_core_index = core_selector % para_assigned_cores.len(); <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let assigned_core_index = core_selector % para_assigned_cores.len();
let committed_core_index = para_assigned_cores[assigned_core_index]; let committed_core_index = para_assigned_cores[assigned_core_index];
</code></pre> <span class="boring">}</span></code></pre></pre>
<h3 id="polkadot-primitive-changes"><a class="header" href="#polkadot-primitive-changes">Polkadot Primitive changes</a></h3> <h3 id="polkadot-primitive-changes"><a class="header" href="#polkadot-primitive-changes">Polkadot Primitive changes</a></h3>
<h4 id="new-candidatedescriptor"><a class="header" href="#new-candidatedescriptor">New <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L482">CandidateDescriptor</a></a></h4> <h4 id="new-candidatedescriptor"><a class="header" href="#new-candidatedescriptor">New <a href="https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/primitives/src/v7/mod.rs#L482">CandidateDescriptor</a></a></h4>
<ul> <ul>
<li>reclaim 32 bytes from <code>collator: CollatorId</code> and 64 bytes from <code>signature: CollatorSignature</code> and rename to <code>reserved1</code> and <code>reserved2</code> fields.</li> <li>reclaim 32 bytes from <code>collator: CollatorId</code> and 64 bytes from <code>signature: CollatorSignature</code> and rename to <code>reserved1</code> and <code>reserved2</code> fields.</li>
<li>take 1 bytes from <code>reserved1</code> for a new <code>version: u8</code> field.</li>
<li>take 2 bytes from <code>reserved1</code> for a new <code>core_index: u16</code> field.</li> <li>take 2 bytes from <code>reserved1</code> for a new <code>core_index: u16</code> field.</li>
<li>take 4 bytes from <code>reserved2</code> for a new <code>session_index: u32</code> field.</li> <li>take 4 bytes from <code>reserved1</code> for a new <code>session_index: u32</code> field.</li>
<li>the <code>reserved1</code> and <code>reserved2</code> fields are zeroed</li> <li>the remaining <code>reserved1</code> and <code>reserved2</code> fields are zeroed</li>
</ul> </ul>
<p>Thew new primitive will look like this:</p> <p>Thew new primitive will look like this:</p>
<pre><code>pub struct CandidateDescriptorV2&lt;H = Hash&gt; { <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct CandidateDescriptorV2&lt;H = Hash&gt; {
/// The ID of the para this is a candidate for. /// The ID of the para this is a candidate for.
para_id: Id, para_id: ParaId,
/// The hash of the relay-chain block this is executed in the context of. /// The hash of the relay-chain block this is executed in the context of.
relay_parent: H, relay_parent: H,
/// Version field. The raw value here is not exposed, instead it is used
/// to determine the `CandidateDescriptorVersion`, see `fn version()`
version: InternalVersion,
/// The core index where the candidate is backed. /// The core index where the candidate is backed.
core_index: CoreIndex, core_index: u16,
/// The session index in which the candidate is backed. /// The session index of the candidate relay parent.
session_index: SessionIndex, session_index: SessionIndex,
/// Reserved bytes. /// Reserved bytes.
reserved1: [u8; 26], reserved25b: [u8; 25],
/// The blake2-256 hash of the persisted validation data. This is extra data derived from /// The blake2-256 hash of the persisted validation data. This is extra data derived from
/// relay-chain state which may vary based on bitfields included before the candidate. /// relay-chain state which may vary based on bitfields included before the candidate.
/// Thus it cannot be derived entirely from the relay-parent. /// Thus it cannot be derived entirely from the relay-parent.
@@ -310,35 +321,39 @@ let committed_core_index = para_assigned_cores[assigned_core_index];
/// The root of a block's erasure encoding Merkle tree. /// The root of a block's erasure encoding Merkle tree.
erasure_root: Hash, erasure_root: Hash,
/// Reserved bytes. /// Reserved bytes.
reserved2: [u8; 64], reserved64b: [u8; 64],
/// Hash of the para header that is being generated by this candidate. /// Hash of the para header that is being generated by this candidate.
para_head: Hash, para_head: Hash,
/// The blake2-256 hash of the validation code bytes. /// The blake2-256 hash of the validation code bytes.
validation_code_hash: ValidationCodeHash, validation_code_hash: ValidationCodeHash,
} }
</code></pre> <span class="boring">}</span></code></pre></pre>
<p>In future format versions, parts of the <code>reserved1</code> and <code>reserved2</code> bytes can be used to include additional information in the descriptor.</p> <p>In future format versions, parts of the <code>reserved1</code> and <code>reserved2</code> bytes can be used to include additional information in the descriptor.</p>
<h4 id="versioned-candidatereceipt-and-committedcandidatereceipt-primitives"><a class="header" href="#versioned-candidatereceipt-and-committedcandidatereceipt-primitives">Versioned <code>CandidateReceipt</code> and <code>CommittedCandidateReceipt</code> primitives:</a></h4> <h4 id="candidate-descriptor-api"><a class="header" href="#candidate-descriptor-api">Candidate descriptor API</a></h4>
<p>We want to decouple the actual representation of the <code>CandidateReceipt</code> from the higher level code. This should make it easier to implement future format versions of this primitive. To hide the logic of versioning the descriptor fields will be private and accessor methods are provided.</p> <p>We want to decouple the actual representation of the <code>CandidateDescriptor</code> from the higher level code. This should make it easier to implement future format versions of this primitive. To hide the logic of versioning the descriptor fields will be private and getter methods are provided for all the fields. </p>
<pre><code>pub enum VersionedCandidateReceipt { <pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
V1(CandidateReceipt), </span><span class="boring">fn main() {
V2(CandidateReceiptV2), </span>impl&lt;H&gt; CandidateDescriptorV2&lt;H&gt; {
}
impl VersionedCandidateReceipt { /// Returns the collator id in the descriptor. Returns `None` if descriptor is at verision 2.
/// Returns the core index the candidate has commited to. pub fn collator(&amp;self) -&gt; Option&lt;CollatorId&gt;;
/// Returns `None` if the candidate receipt is the old version (v1).
fn core_index() -&gt; Option&lt;CoreIndex&gt;;
/// Returns the session index of the candidate relay parent. /// Returns the collator signature in the descriptor. Returns `None` if descriptor is at verision 2.
fn session_index() -&gt; Option&lt;CoreIndex&gt;; pub fn signature(&amp;self) -&gt; Option&lt;CollatorSignature&gt;;
/// Returns the core index of the descriptor. Returns `None` if the descriptor is at version 1.
pub fn core_index(&amp;self) -&gt; Option&lt;CoreIndex&gt;;
/// Returns the session index of the descriptor. Returns `None` if the descriptor is at version 1.
pub fn session_index(&amp;self) -&gt; Option&lt;SessionIndex&gt;;
/// ... /// ...
} }
</code></pre> <span class="boring">}</span></code></pre></pre>
<p>A manual decode <code>Decode</code> implementation is required to account for version detection and constructing the appropriate variant.</p> <p>A manual decode <code>Decode</code> implementation is required to account for version detection and constructing the appropriate variant.</p>
<h3 id="parachain-block-validation"><a class="header" href="#parachain-block-validation">Parachain block validation</a></h3> <h3 id="parachain-block-validation"><a class="header" href="#parachain-block-validation">Parachain block validation</a></h3>
<h4 id="node"><a class="header" href="#node">Node</a></h4>
<p>Backers will make use of the core index information to validate the blocks during backing and reject blocks if:</p> <p>Backers will make use of the core index information to validate the blocks during backing and reject blocks if:</p>
<ul> <ul>
<li>the <code>core_index</code> in descriptor does not match the one determined by the <code>UMPSignal::SelectCore</code> message</li> <li>the <code>core_index</code> in descriptor does not match the one determined by the <code>UMPSignal::SelectCore</code> message</li>
@@ -346,6 +361,8 @@ impl VersionedCandidateReceipt {
<li>the <code>session_index</code> is not equal to the session of the <code>relay_parent</code> in the descriptor</li> <li>the <code>session_index</code> is not equal to the session of the <code>relay_parent</code> in the descriptor</li>
</ul> </ul>
<p>If core index (and session index) information is not available (backers got an old candidate receipt), there will be no changes compared to current behaviour.</p> <p>If core index (and session index) information is not available (backers got an old candidate receipt), there will be no changes compared to current behaviour.</p>
<h4 id="runtime"><a class="header" href="#runtime">Runtime</a></h4>
<p>The runtime will also perform the above checks and reject invalid candidates.</p>
<h2 id="drawbacks"><a class="header" href="#drawbacks">Drawbacks</a></h2> <h2 id="drawbacks"><a class="header" href="#drawbacks">Drawbacks</a></h2>
<p>The only drawback is that further additions to the descriptor are limited to the amount of remaining unused space.</p> <p>The only drawback is that further additions to the descriptor are limited to the amount of remaining unused space.</p>
<h2 id="testing-security-and-privacy"><a class="header" href="#testing-security-and-privacy">Testing, Security, and Privacy</a></h2> <h2 id="testing-security-and-privacy"><a class="header" href="#testing-security-and-privacy">Testing, Security, and Privacy</a></h2>
@@ -355,22 +372,22 @@ impl VersionedCandidateReceipt {
<h2 id="performance"><a class="header" href="#performance">Performance</a></h2> <h2 id="performance"><a class="header" href="#performance">Performance</a></h2>
<p>The expectation is that performance impact is negligible for sending and processing the UMP message has negligible performance impact in the runtime as well as on the node side.</p> <p>The expectation is that performance impact is negligible for sending and processing the UMP message has negligible performance impact in the runtime as well as on the node side.</p>
<h2 id="ergonomics"><a class="header" href="#ergonomics">Ergonomics</a></h2> <h2 id="ergonomics"><a class="header" href="#ergonomics">Ergonomics</a></h2>
<p>Parachain that use elastic scaling must send the separator empty message followed by the <code>UMPSignal::SelectCore</code> only after sending all of the UMP XCM messages.</p> <p>It is mandatory for elastic parachains to switch to the new receipt format. It is optional but desired that all parachains
switch to the new receipts for providing the session index for disputes.</p>
<p>Once this RFC is implemented the parachain runtime and node must not require any manual changes to use it, except if the parachain wants to change the <code>ClaimQueueOffset</code> that is used to determine the core index. </p>
<h2 id="compatibility"><a class="header" href="#compatibility">Compatibility</a></h2> <h2 id="compatibility"><a class="header" href="#compatibility">Compatibility</a></h2>
<h3 id="versioning"><a class="header" href="#versioning">Versioning</a></h3> <p>The proposed changes are backwards compatible in general, but additional care must be taken by waiting for enough validators to upgrade before the validators and runtime start accepting the new candidate receipts.</p>
<p>At this point there is a simple way to determine the version of the receipt, by testing for zeroed reserved bytes in the <h3 id="runtime-1"><a class="header" href="#runtime-1">Runtime</a></h3>
descriptor. Supporting future changes will require a <code>u8</code> version field to be introduced in the reserved space. We consider the current version to be 0 and the version check implicitly done when checking for reserved space to be zeroed.</p>
<h3 id="runtime"><a class="header" href="#runtime">Runtime</a></h3>
<p>The first step is to remove collator signature checking logic in the runtime, but keep the node side collator signature <p>The first step is to remove collator signature checking logic in the runtime, but keep the node side collator signature
checks. </p> checks. </p>
<p>The runtime must be upgraded to the new primitives before any collator or node are allowed to use the new candidate receipts format. </p> <p>The runtime must be upgraded to the new primitives before any collator or node are allowed to use the new candidate receipts format. </p>
<h3 id="validators"><a class="header" href="#validators">Validators</a></h3> <h3 id="validators"><a class="header" href="#validators">Validators</a></h3>
<p>To ensure a smooth launch, a new node feature is required. <p>To ensure a smooth launch, a new node feature is required.
The feature acts as a signal for supporting the new candidate receipts on the node side and can only be safely enabled if at least 2/3 of the validators are upgraded.</p> The feature acts as a signal for supporting the new candidate receipts on the node side and can only be safely enabled if at least 2/3 of the validators are upgraded.</p>
<p>Once enabled, the validators will skip checking the collator signature when processing the candidate receipts and verify the <code>CoreIndex</code> and <code>SessionIndex</code> fields if present in the receipit.</p> <p>Once enabled, the validators will skip checking the collator signature when processing the candidate receipts and verify the <code>CoreIndex</code> and <code>SessionIndex</code> fields if present in the receipt.</p>
<p>No new implementation of networking protocol versions for collation and validation are required.</p> <p>No new implementation of networking protocol versions for collation and validation are required.</p>
<h3 id="parachains"><a class="header" href="#parachains">Parachains</a></h3> <h3 id="parachains"><a class="header" href="#parachains">Parachains</a></h3>
<p><code>CoreIndex</code> commitments are needed only by parachains using elastic scaling. Just upgrading the collator node and runtime should be sufficient and possible without manual changes.</p> <p>The implementation of this RFC will supersede the Elastic MVP feature that relies on injecting a core index in the <code>validator_indices</code> fields of the <code>BackedCandidate</code> primitive. Elastic parachains must upgrade to use the new receipt format.</p>
<h3 id="tooling"><a class="header" href="#tooling">Tooling</a></h3> <h3 id="tooling"><a class="header" href="#tooling">Tooling</a></h3>
<p>Any tooling that decodes UMP XCM messages needs an update to support or ignore the new UMP messages, but they should be fine to decode the regular XCM messages that come before the separator.</p> <p>Any tooling that decodes UMP XCM messages needs an update to support or ignore the new UMP messages, but they should be fine to decode the regular XCM messages that come before the separator.</p>
<h2 id="prior-art-and-references"><a class="header" href="#prior-art-and-references">Prior Art and References</a></h2> <h2 id="prior-art-and-references"><a class="header" href="#prior-art-and-references">Prior Art and References</a></h2>
@@ -379,7 +396,8 @@ The feature acts as a signal for supporting the new candidate receipts on the no
<p>N/A</p> <p>N/A</p>
<h2 id="future-directions-and-related-material"><a class="header" href="#future-directions-and-related-material">Future Directions and Related Material</a></h2> <h2 id="future-directions-and-related-material"><a class="header" href="#future-directions-and-related-material">Future Directions and Related Material</a></h2>
<p>The implementation is extensible and future proof to some extent. With minimal or no breaking changes, additional fields can be added in the candidate descriptor until the reserved space is exhausted</p> <p>The implementation is extensible and future proof to some extent. With minimal or no breaking changes, additional fields can be added in the candidate descriptor until the reserved space is exhausted</p>
<p>Once the reserved space is exhausted, versioning will be implemented. The candidate receipt format will be versioned. Versioning should also be implemented for the validation function, inputs and outputs (<code>CandidateCommitments</code>).</p> <p>At this point there is a simple way to determine the version of the receipt, by testing for zeroed reserved bytes in the
descriptor. Future versions of the receipt can be implemented and identified by using the <code>version</code> field of the descirptor introduced in this RFC.</p>
</main> </main>
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long