From 006a9ff07c3d3bc5316c6bf63b05e966e694cc2d Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 20 Sep 2023 09:29:35 +0100 Subject: [PATCH 01/47] Add CoreJam draft --- text/corejam.md | 528 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 528 insertions(+) create mode 100644 text/corejam.md diff --git a/text/corejam.md b/text/corejam.md new file mode 100644 index 000000000..0a470755d --- /dev/null +++ b/text/corejam.md @@ -0,0 +1,528 @@ +# RFC-XXXX: "CoreJam" + +| | | +| --------------- | ------------------------------------------------------------------------------------------- | +| **Start Date** | 11 September 2023 | +| **Description** | Parallelised, decentralised, permissionless state-machine based on a multistage Collect-Refine-Join-Accumulate model. | +| **Authors** | Gavin Wood | + + +## Summary + +This is a proposal to fundamentally alter the workload done on the Polkadot Relay-chain, both in terms of that which is done "on-chain", i.e. by all Relay Chain Validators (*Validators*) as well as that which is done "in-core", i.e. distributed among subsets of the Validators (*Validator Groups*). The target is to create a model which closely matches the underlying technical architecture and is both generic and permissionlessly extensible. + +In the proposed model, code is stored on-chain with two entry-points. Workloads are collated and processed in-core (and thus parallelized) using one entry-point, whereas the refined outputs of this processing are gathered together and an on-chain state-machine progressed according to the other. + +While somewhat reminiscent of the Map-Reduce paradigm, a comprehensive analogy cannot be taken: the in-core processing code does not transform a set of inputs, but is rather used to refine entirely arbitrary input data collected by some third-party. Instead, and in accordance, we term it *Collect-Refine-Join-Accumulate*. + +## Motivation + +Polkadot was originally designed as a means of validating state transitions of Webassembly-defined state machines known as *Parachain Validation Functions*. These state machines were envisioned to be long-lived (of the order of years) and transitioning continuously, at the "full capacity" of modern single-threaded hardware held in consensus over the internet, and in isolation to any other such state machines. + +Having actually built Polkadot, it became clear that the flexibility of the machinery implementing it allowed for a more diverse set of usage patterns and models. Parathreads, which came to be known as *On-Demand Parachains* (ODP) is one such model. This was underlined by other proposals to allow for a more decentralised administration of how the underlying Polkadot Core resource is procured, in particular *Agile Coretime*. + +More recently, the idea of having small to medium size programs executing without its own surrounding blockchain using only Relay-chain resources has been discussed in detail primarily around the *Coreplay* proposal. It therefore seems short-sighted to assume other models could not exist for utilizing the Relay-chain's "Core" resource. Therefore in much the same way that Agile Coretime originally strived to provide the most general model of *procuring* Relay-chain's Core resource, it seems sensible to strive to find a similarly general model for *utilizing* this resource, one minimizing the difference between the valuable function of the Validators and the service offered by Polkadot. + +Beyond delivering additional value through the increased potential for use-cases that this flexibility allows, our motivation extends to gaining stability: a future-proof platform allowing teams to build on it without fear of high maintenance burden, continuous bitrot or a technological rug-pull at some later date. Secondly, we are motivated by reducing barriers for new teams, allowing the Polkadot platform to harness the power of the crowd which permissionless systems uniquely enable. + +Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the classical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. + +## Requirements + +In order of importance: + +1. The proposal must be compatible, in principle, with the preexisting parachain model. +2. The proposal must facilitate the implementation of Coreplay. +3. The proposal must be compatible with Agile Coretime, as detailed in RFC#0001. +4. Implementation of the proposal should need minimal changes to all production logic. +5. Utilization of Coretime must be accessible. +6. Utilization of Coretime must be permissionless. +7. The nature of Coretime should closely match the nature of resources generated by Polkadot. +8. Minimal opinionation should be introduced over the format, nature and usage of Coretime. + +## Stakeholders + +1. Anyone with exposure to the DOT token economy. +2. Anyone wanting to create decentralised/unstoppable/resilient applications. +3. Teams already building on Polkadot. + +## Explanation + +The idea of *Proof-of-Validity* and *Parachain Validation Function* as first-class concepts in the Polkadot protocol is removed. These are now specializations of more general concepts. + +We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Class Trie*. + + +```rust +mod v0 { + const PROGRESS_WEIGHT_PER_PACKAGE: Weight = MAX_BLOCK_WEIGHT * 3 / 4; + type WorkClass = u32; + type WorkPayload = Vec; + struct WorkItem { + class: WorkClass, + payload: WorkPayload, + } + type MaxWorkItemsInPackage = ConstU32<16>; + struct WorkPackage { + authorization: Vec, + progressions: BoundedVec<(WorkClass, Weight), MaxWorkItemsInPackage>, + items: BoundedVec, + } +} +type MaxWorkPackageSize = ConstU32<5 * 1024 * 1024>; +struct EncodedWorkPackage { + version: u32, + encoded: BoundedVec, +} +impl TryFrom for v0::WorkPackage { + type Error = (); + fn try_from(e: EncodedWorkPackage) -> Result { + match e.version { + 0 => Self::decode(&mut &e.encoded[..]).map_err(|_| ()), + _ => Err(()), + } + } +} +``` + +A *Work Package* is an *Authorization* together with a series of *Work Items*, limited in plurality, versioned and with a maximum encoded size. There is a third field `progressions` to provide weight-limit information for the final *Accumulate* stage. It must fulfill a constraint that the sum over all `Weight` items (i.e. the second item in the `Vec`'s tuple) must be no greater than `PROGRESS_WEIGHT_PER_PACKAGE`. + +Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). + +### Authorization and Authorizers + +An *Authorizer* is parameterized procedure: + +```rust +type CodeHash = [u8; 32]; +type AuthParamSize = ConstU32<1024>; +type AuthParam = BoundedVec; +struct Authorizer { + code_hash: CodeHash, + param: AuthParam, +} +``` + +The `code_hash` of the Authorizer is assumed to be the hash of some code accessible in the Relay-chain's Storage pallet. The procedure itself is called the *Authorization Procedure* (`AuthProcedure`) and is expressed in this code (which must be capable of in-core VM execution). Its entry-point prototype is: + +```rust +fn is_authorized(param: &AuthParam, package: &WorkPackage) -> bool; +``` + +If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. + +A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. + +Since encoded `Authorizer` values may be fairly large (up to 1,038 bytes here), they may not be a drop-in replacement for the `ParaId`/`TaskId` used at present in the Agile Coretime interface. Because of this, we provide a lookup mechanism allowing a much shorter `AuthId` to be used within the Coretime scheduling messaging. Conveniently, this is precisely the same datatype size (32-bit) as a `ParaId`/`TaskId`. + +There is an Authorizations Pallet which stores the association. Adding a new piece of code is permissionless but requires a deposit commensurate with its size. + +```rust +type AuthId = u32; +type Authorizers = StorageMap; +``` + +An *Authorization* is simply a blob which helps the Authorizer recognize a properly authorized Work Package. No constraints are placed on Authorizers over how they may interpret this blob. Expected authorization content includes signatures, Merkle-proofs and more exotic succinct zero-knowledge proofs. + +_(Note: depending on future Relay-chain Coretime scheduling implementation concerns, a window of Relay-chain blocks)._ + +The need of validators to be rewarded for doing work they might reasonably expect to be useful competes with that of the Coretime procurers to be certain to get work done which is useful to them. In Polkadot 1.0, validators only get rewarded for PoVs ("work packages") which do not panic or overrun. This ensures that validators are well-incentivized to ensure that their computation is useful for the assigned parachain. This incentive model works adequately where all PVF code is of high quality and collators are few and static. + +However with this proposal (and even the advent of on-demand parachains), validators have little ability to identify a high-quality Work Package builder and the permissionless design means a greater expectation of flawed code executing in-core. Because of this, we make a slightly modified approach: Work Packages must have a valid Authorization, i.e. the Coretime-assigned `is_authorized` returns `true` when provided with the Work Package. However, Validators get rewarded for *any* such authorized Work Package, even one which ultimately panics or overruns on its evaluation. + +This ensures that Validators do a strictly limited amount of work before knowing whether they will be rewarded and are able to discontinue and attempt other candidates earlier than would otherwise be the case. There is the possibility of wasting Coretime by processing Work Packages which result in error, but well-written authorization procedures can mitigate this risk by making a prior validation of the Work Items. + +### Collect-Refine + +The `refine` function is implemented as an entry-point inside a code blob which is stored on-chain and whose hash is associated with the Work Class. + +```rust +type ClassCodeHash = StorageMap; +``` + +```rust +type WorkOutputLen = ConstU32<1024>; +type WorkOutput = BoundedVec; +fn refine(payload: WorkPayload) -> WorkOutput; +``` + +Both `refine` and `is_authorized` are only ever executed on-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. + +Several host functions, largely in line with the *PVF* host functions, are supplied. Two additional ones include: + +```rust +/// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. +fn lookup(hash: [u8; 32]) -> Vec; +/// Determine the state root of the block at given `height`. +fn state_root(height: u32) -> Option<[u8; 32]>; +``` + +Other host functions will allow for the possibility of executing a WebAssembly payload (for example, a Parachain Validation Function) or instantiating and entering a subordinate RISCV VM (for example for Actor Progressions). + +When applying `refine` from the client code, we must allow for the possibility that the VM exits unexpectedly or does not end. If this happens, then the Work Item is invalidated. Thus we define a type `WorkResult`: + +```rust +enum WorkError { + Timeout, + Panic, +} +type WorkResult = Result; +fn apply_refine(item: WorkItem) -> WorkResult; +``` + +Each Relay-chain block, every Validator Group representing a Core which is assigned work provides up to one Work Result coherent with the assignment of that Core. Validators are rewarded when they take part in their Group and process a Work Package. This culminates in providing a *Work Result* to the Relay-chain block author. If no Work Result is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. + +### Join-Accumulate + +Join-Accumulate is a second stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. + +The Join-Progess stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions and oversee message queue transitions. + +The user-supplied `accumulate` function defines the elective portion of the Join-Accumulate stage. This is executed in a *metered* format, meaning it must be able to be executed in a sandboxed and non-deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. + +Practically speaking, we may allow a similar VM execution metering system similar to that for the `refine` execution, whereby we do not require a strictly deterministic means of interrupting, but do require deterministic metering and only approximate interruption. This would mean that full-nodes and Relay-chain validators could be made to execute some additional margin worth of computation without payment, though any attack could easily be mitigated by attaching a fixed cost (either economically or in weight terms) to an VM invocation. + +The function signature to the `accumulate` entry-point in the Work Class's code blob is: + +```rust +fn accumulate(results: Vec<(Authorization, Vec<(ItemHash, WorkResult)>)>); +type ItemHash = [u8; 32]; +``` + +As stated, there is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. This is the sum of all `progressions` fields whose first item is the particular Work Class across all scheduled Work Packages. + +Work Items are identified by their hash (`ItemHash`). We provide both the authorization of the package and the item identifers and their results in order to allow the `refine` logic to take appropriate action in the case that an invalid Work Item was issued. + +_(Note for later: We may wish to provide a more light-client friendly Work Item identifier than a simple hash; perhaps a Merkle root of equal-size segments.)_ + +Read-access to the entire Relay-chain state is allowed, but no direct write access may be provided since `refine` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. + +```rust +fn checkpoint() -> Weight; +fn weight_remaining() -> Weight; +fn get_work_storage(key: &[u8]) -> Result>; +fn get_work_storage_len(key: &[u8]); +fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; +fn remove_work_storage(key: &[u8]); +``` + +Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `refine` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. + +Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). + +Other host functions, including some to access Relay-chain hosted services such as the Balances and Storage Pallet may also be provided commensurate with this executing on-chain. + +_(Note for discussion: Should we be considering light-client proof size at all here?)_ + +We can already imagine two kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0) and *Actor Progression* (as per Coreplay). Given how abstract the model is, one might reasonably expect many more. + +### Relay-chain Storage Pallet + +There is a general need to be able to reference large, immutable and long-term data payloads both on-chain and in-core. This is both the case for fixed-function logic such as fetching the VM code for `refine` and `accumulate` as well as from within Work Packages themselves. + +Owing to the potential for forks and disputes to happen beyond the scope of initial validation, there are certain quite subtle requirements over what data held on-chain may be utilized in-core. Because of this, it makes sense to have a general solution which is known to be safe to use in all circumstances. We call this solution the *Storage Pallet*. + +The Storage Pallet provides a simple API, accessible to untrusted code through host functions & extrinsics and to trusted Relay-chain code via a trait interface. + +```rust +trait Storage { + /// Immutable function to attempt to determine the preimage for the given `hash`. + fn lookup(hash: &[u8; 32]) -> Option>; + + /// Allow a particular preimage to be `provide`d. + /// Once provided, this will be available through `lookup` until + /// `unrequest` is called. + fn request(hash: &[u8; 32], len: usize) -> bool; + /// Remove request that some data be made available. If the data was never + /// available or the data will remain available due to another request, + /// then `false` is returned and `expunge` may be called immediately. + /// Otherwise, `true` is returned and `expunge` may be called in + /// 24 hours. + fn unrequest(hash: &[u8; 32]) -> bool; + + // Functions used by implementations of untrusted functions; such as + // extrinsics or host functions. + + /// Place a deposit in order to allow a particular preimage to be `provide`d. + /// Once provided, this will be available through `lookup` until + /// `unrequest_untrusted` is called. + fn request_untrusted(depositor: &AccountId, hash: &[u8; 32], len: usize); + /// Remove request that some data be made available. If the data was never + /// available or the data will remain available due to another request, + /// then `false` is returned and `expunge_untrusted` may be called immediately. + /// Otherwise, `true` is returned and `expunge_untrusted` may be called in + /// 24 hours. + fn unrequest_untrusted(depositor: &AccountId, hash: &[u8; 32]) -> bool; + + // Permissionless items utilizable directly by an extrinsic or task. + + /// Provide the preimage of some requested hash. Returns `Some` if its hash + /// was requested; `None` otherwise. + /// + /// Usually utilized by an extrinsic and is free if `Some` is returned. + fn provide(preimage: &[u8]) -> Option<[u8; 32]>; + /// Potentially remove the preimage of `hash` from the chain when it was + /// unrequested using `unrequest`. `Ok` is returned iff the operation is + /// valid. + /// + /// Usually utilized by a task and is free if it returns `Ok`. + fn expunge(hash: &[u8; 32]) -> Result<(), ()>; + /// Return the deposit associated with the removal of the request by + /// `depositor` using `unrequest_untrusted`. Potentially + /// remove the preimage of `hash` from the chain also. `Ok` is returned + /// iff the operation is valid. + /// + /// Usually utilized by a task and is free if it returns `Ok`. + fn expunge_untrusted(depositor: &AccountId, hash: &[u8; 32]) -> Result<(), ()>; + + /// Equivalent to `request` followed immediately by `provide`. + fn store(data: &[u8]) -> [u8; 32]; +} +``` + +Internally, data is stored with a reference count so that two separate usages of `store` need not be concerned about the other. + +Every piece of data stored for an untrusted caller requires a sizeable deposit. When used by untrusted code via a host function, the `depositor` would be set to an account controlled by the executing code (e.g. the Work Class's sovereign account). + +Removing data happens in a two-phase procedure; first the data is unrequested, signalling that calling `lookup` on its hash may no longer work (it may still work if there are other +requests active). 24 hours following this, the data is expunged with a second call which, actually removes the data from the chain assuming no other requests for it are active. + +Only once expunge is called successfuly is the deposit returned. If the data was never provided, or is additional requests are still active, then expunge may be called immediately after a successful unrequest. + +### Notes on Agile Coretime + +Crucially, a *Task* is no longer a first-class concept. Thus the Agile Coretime model, which in large part allows Coretime to be assigned to a Task Identifier from the Coretime chain, would need to be modified to avoid a hard dependency on this. + +In this proposal, we replace the concept of a Task with a more general ticketing system; Coretime is assigned to an *Authorizer* instead, a parameterized function. This would allow a succinct *Authorization* (i.e. a small blob of data) to be included in the Work Package which, when fed into the relevant Authorizer function could verify that some Work Package is indeed allowed to utilize that Core at (roughly) that time. A simple proof system would be a regular PKI signature. More complex proof systems could include more exotic cryptography (e.g. multisignatures or zk-SNARKs). + +In this model, we would expect any authorized Work Packages which panic or overrun to result in a punishment to the specific author by the logic of the Work Class. + +### Notes for migrating from a Parachain-centric model + +All Parachain-specific data held on the Relay-chain including the means of tracking the Head Data and Code would be held in the Parachains Work Class (Child) Trie. The Work Package would be essentially equivalent to the current PoV blob, though prefixed by the Work Class. `refine` would prove the validity of the parachain transition described in the PoV which is the Work Package. The Parachains Work Output would provide the basis for the input of what is currently termed the Paras Inherent. `accumulate` would identify and resolve any colliding transitions and manage message queue heads, much the same as the current hard-coded logic of the Relay-chain. + +We should consider utilizing the Storage Pallet for Parachain Code and store only a hash in the Parachains Work Class Trie. + +### Notes for implementing the Actor Progression model + +Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Work Class Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Work Class Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. + +## Performance, Ergonomics and Compatibility + +This system must be broadly compatible with our existing Parachain Validation Function/Proof-of-Validity model, however a specific feasibility study into transitioning/migration has yet to be completed. + +To aid swift deployment, the Relay-chain may retain its existing parachain-specific logic "hardcoded", and the Coregap logic added separately, with Work Class "zero" being special-cased to mean "the hard-coded Parachain logic". + +## Testing, Security and Privacy + +Standard Polkadot testing and security auditing applies. + +The proposal introduces no new privacy concerns. + +## Future Directions and Related Material + +Important considerations include: + +1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the subpackages. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Work Class State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). + +## Drawbacks, Alternatives and Unknowns + +None at present. + +## Prior Art and References + +None. + + + +# Chat +for this we need a pallet on the RC to allow arbitrary data to be stored for a deposit, with a safeguard that it would remain in RC state for at least 24 hours (in case of dispute); and a host function to allow the PoV to reference it. +this issue is that for fast-changing data, you'd need to store all of the different images for 24 hours each. +this would quickly get prohibitive. +Yeah, but then we can not give these tasks that much memory. Maybe around 0.5 MiB to 1 MiB (depending on how well they compress) +yeah +tasks which expect to execute alone could get ~2MB. +Gav +an alternative would be to require the author-network to compute the precise output itself and send it to the storage chain separately. +and if we do this, then up to around 4.5MB. +it's not much by today's desktop standards, but it still beats the shit out of smart contracts. +In reply to +Gav +Gav +and if we do this, then up to around 4.5MB. +Why can we then double it? What do I miss? +5MB PoV limit. +best case you need to provide the full pre-state (with which to initialize the VM) and a hash of the post-state (to verify computation) +however, it's not clear where you get the next block's pre-state from. +one solution is to provide both pre- and post-state into the PoV and piggy-back on Polkadot's 24-hour availability system +(as long as you build the next block at most 24 hours from the last) +You can get the next state by re executing? +Or keep it in your local cache or whatever +but you don't necessarily have other tasks at that time. +or the RC state. +Ahh I see +or, indeed, the pre-state. +PoV disappears after 24 hours. +We can not recalculate all the state +no +this means an average of 5MB / 2 memory bandwidth per block. +minus a bit for smaller tasks to coordinate with gives 2 - 2.5MB. +But if we put the post state into availability system it should work as well? +but we assume the PoV accounts for All of the RCV's resources. +if it doesn't then we should just increase the PoV size. +Yeah fine +i.e. if the RCV can handle 5MB PoV (availability) plus 5MB additional availability, then isn't it just easier to say 10MB PoV? +I think the limit currently is just from the networking side +Aka we could not even really handle these big PoVs +But with async backing it should now be possible +maybe i'm wrong here - if the limit of 5MB PoV is less about availability and more about transport from the collator, then sure, we can split it into a PoV limit and an availability limit +and have 5MB PoV and 10MB availability. +but i assumed that the bottleneck was just availability. +definitely an assumption to test +Yeah we should find out. Maybe I probably neglecting the erasure coding +since there is a difference between data from some other guy (pre-state) and data which you generate yourself (post-state) +assuming i'm right about the bottleneck, then the best we could do without some form of paging (which should be easy enough to implement with jan's help) is having the post-state be computed on the author-network and placed in the storage chain. +jan says that paging is pretty trivial to customise on his RISCV VM. +just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. +Gav +just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. +Yeah, that was also my idea. +But yeah, for the beginning we can probably start with 0.5MiB of memory +we have 16KB pages currently, and we'll already likely be doing something similar for running multiple stacks +Basti.await +But yeah, for the beginning we can probably start with 0.5MiB of memory +yup +definitely enough for a PoC. +but we will still need a new PoV format. +i.e. where we can: +a) request arbitrary data from the RCTaskStorage pallet (which guarantees data placed on it cannot be removed for 24 hours after last read). +b) progress a particular task (in whatever order) with weight spec +c) provide data into a particular task (in whatever order) with weight spec +we might also have a more abstract PoV protocol which allows for different classes of task. +and specifies in Wasm or whatever exactly how to interpret a PoV +then we reformulate the Parachain PoV into this "smart contract". +and we would create a new Task PoV format as just another instance within this overarching protocol. +e.g. this protocol could define UMP, DMP, XCMP and the initial version of the Actors-PoV format might reasonably not include this logic. +but an upgrade later could allow actors to use this stuff. +the main difference with just "hardcoding" it into the RC logic directly is that it could be permissionless - other teams could come up with their own PoV formats and protocols to determine validity. +ok so i've thought about it a bit. +i think there's a route forward for what i referenced up there as "generic PoVs". +in fact they cease to be "PoVs" at this point. +they're just "Work Packages" since you're not proving anything, you're just using the RCVs for compute. +we would define the concept of a Work Class. +all WPs must have a WT. the WT defines what it means to interpret/execute the WP. +we'd initially have two WTs: Parachains and Tasks. +WTs would be permissionless and would come with two main blobs; one (which I'll call map) which can still be in Wasm (since it runs off-chain at a level higher than the PVF) and one (called reduce) which must be strictly metered (so RISCV or very slow Wasm). +as the name suggests, the first represents map and the second represents reduce of a map-reduce pattern. +the first takes the WP as an argument and can inspect any data held in the RCTaskStore. +it then either panics (invalid) or returns a fixed-size (or rather known-maximum-length) blob. +all such blobs and panics are batched up into a Vec and fed into a WT-defined reduce function, with its max_weight = to the block_weight multiplied by the ratio of cores used for this WT compared to all cores. +all WTs have their own child trie. +only this reduce function may alter data in that trie. +for the Parachain WT, this child trie would include all the parachain-specific data (code and state); the ParachainWP output type would basically be the per-chain paras-inherent arguments, and so the PWT reduce function would basically be the paras-inherent logic. +for the Actors WT, this child trie would include Actor specific stuff like codehash (the actual code would be stored in the RCTaskStore) and RISCVVM memory hash, as well as sequence number. +The Actor output would include enough information on which actor-combinations got (maybe) progressed to allow the proper progressions to be recorded in the Actor Child Trie by the reduce function. essentially just the logic i already define in the RFC. +So the Actor map function would interpret the WorkPackage as a manifest and fetch all actor code, initialise each Actor's VM and start them with the entry points according to the manifest. +with this model, anyone could add their own Work Classs. +So if RobK/Arkadiy/Dave can implement the spec, we can focus on writing up the Actor-based map and reduce function. They need not be concerned with Actors at all. +sound sensible? +Good question :P +So the map would have access to fetch the code from the relay chain? +And the map would be trusted code? +That is the same for every WT? +neither map nor reduce are trusted +Gav +neither map nor reduce are trusted +Ahh yeah, you said it +map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. +But the code executed in reduce is defined by the output of map? +yes +Gav +map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. +The code to execute will be referenced by the code hash and this code hash needs to be "somewhere". Currently we store in the relay chain state +yeah, the code would need to be in the "RCTaskStore" (should be the RCWorkStore, i guess) +there is the question about the WorkType Child Trie +This is the child trie you mentioned? +(The RCTaskStore) +RCWorkStore is the on-chain paid data repository which guarantees data remains available for 24 hours after removal +there is also the WorkTypeChildTrie. +i guess this would need to guarantee some sort of dispute-availability also. +Gav +there is also the WorkTypeChildTrie. +This is stored in the RC state? +yes +it's a child trie. +yeah, just wanted to be sure :P +Gav +i guess this would need to guarantee some sort of dispute-availability also. +Yeah for sure. If we need the data as input of the validation +we might need to limit this. +yeah, i think we don't need any of this data. +And map to reduce would be something like Vec }>? +map output would basically be Vec +where struct ProvisionalProgression { actor: ActorId, code: [u8;32], prestate: [u8;32], poststate: [u8;32] } +then reduce would take a Vec> +reduce would check there are no collisions (same actor progressing from same poststate), that the code executed is the expected code hash of the actor and that the prestate fits into a coherent progression of the actor (which might not be the old poststate since the actor could be doing multiple progressions in a single RC block). +So you want to execute the actor code from within reduce? +no +actor code gets executed off-chain in map. +output of map informs reduce what it has done. +it's up to reduce to figure out how to combine all of this into a coherent RC state update. +But when is reduce executed? As part of the RC block execution/building? +yeah +reduce is on-chain, +it accepts a Vec +WorkTypeOutput is an output specific to WorkType, i.e. WorkTypeOutput in fn map(WorkPackage) -> Result +Okay, I see where you want to go. +Gav +then reduce would take a Vec> +Why this, because there can be multiple WorkPackages being progressed in one RC? +yes, one for every core. +those ProvisionalProgressions appearing together in an inner Vec have been co-scheduled. +it might possibly make a difference inside of reduce to know what progressions have happened together on the same core. +Okay. So reduce is getting all the WorkOutputs of all cores for one WT? +precisely. +one WT would be the whole of parachains, UMP, DMP, XCMP. +Yeah +another WT would be the whole actor environment. +Actors and parachains will never be able to talk to each other? +yeah, we can imagine actor-v2 WT which includes the possibility of XCMP/DMP/UMP. +but i would get a basic version of actors done first. +Basti.await +Actors and parachains will never be able to talk to each other? +I more meant "co-scheduled" +just to show it's possible. +In reply to +Basti.await +Basti.await +I more meant "co-scheduled" +perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions +but we should be able to get most of the benefits leaving it as XCMP +if actors work as well as we expect, then the need for chains will dramatically reduce +In reply to +Basti.await +Gav +perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions +Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" +But maybe some simple "was updated in RC block X" should be enough +But yeah, what you propose sounds reasonable +In reply to +Gav +Basti.await +Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" +yeah, i think we would need to provide some sort of hard-coded-function on the RC to migrate between WTs in this case. +parachains couldn't be part of two WTs at once +Okay +but again, i don't see too much benefit in synchronous composability between actors and chains +Yeah +Just wanted to ask +and it complicates things massively +And I think chains are an old concept anyway with coreplay :P +quite. +the level of experimentation this would provide is pretty immense +Yes +This would also quite help for stuff like "elastic scaling" etc. +basically, just turn polkadot into a global, secure map-reduce computer. +We could just experiment +As a new WT +yup \ No newline at end of file From d7ec1654bd3797215c2ccfcbdcb20cae3790a0d6 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 21 Sep 2023 20:52:20 +0100 Subject: [PATCH 02/47] weight and prune --- text/corejam.md | 163 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 134 insertions(+), 29 deletions(-) diff --git a/text/corejam.md b/text/corejam.md index 0a470755d..04124b228 100644 --- a/text/corejam.md +++ b/text/corejam.md @@ -4,7 +4,7 @@ | --------------- | ------------------------------------------------------------------------------------------- | | **Start Date** | 11 September 2023 | | **Description** | Parallelised, decentralised, permissionless state-machine based on a multistage Collect-Refine-Join-Accumulate model. | -| **Authors** | Gavin Wood | +| **Authors** | Gavin Wood, Robert Habermeier, Bastian Köcher | ## Summary @@ -48,6 +48,8 @@ In order of importance: ## Explanation +**CoreJam is a general model for utilization of Polkadot Cores. It is a mechanism by which Work Packages are communicated, authorized, computed and verified, and their results gathered, combined and accumulated into particular parts of the Relay-chain's state.** + The idea of *Proof-of-Validity* and *Parachain Validation Function* as first-class concepts in the Polkadot protocol is removed. These are now specializations of more general concepts. We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Class Trie*. @@ -65,7 +67,6 @@ mod v0 { type MaxWorkItemsInPackage = ConstU32<16>; struct WorkPackage { authorization: Vec, - progressions: BoundedVec<(WorkClass, Weight), MaxWorkItemsInPackage>, items: BoundedVec, } } @@ -85,13 +86,21 @@ impl TryFrom for v0::WorkPackage { } ``` -A *Work Package* is an *Authorization* together with a series of *Work Items*, limited in plurality, versioned and with a maximum encoded size. There is a third field `progressions` to provide weight-limit information for the final *Accumulate* stage. It must fulfill a constraint that the sum over all `Weight` items (i.e. the second item in the `Vec`'s tuple) must be no greater than `PROGRESS_WEIGHT_PER_PACKAGE`. +A *Work Package* is an *Authorization* together with a series of *Work Items*, limited in plurality, versioned and with a maximum encoded size. Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). -### Authorization and Authorizers +Though this process happens entirely in consensus, there are two main consensus environments at play, _in-core_ and _on-chain_. We therefore partition the progress into two pairs of stages: Collect & Refine and Join & Accumulate. + +### Collect-Refine + +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these attestations form the basis for inclusion on-chain and integration into the Relay-chain's state (in the following stages). + +#### Collection and `is_authorized` -An *Authorizer* is parameterized procedure: +Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a pre-requisite for a Work Package to be included included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. + +An *Authorizer* is a parameterized procedure: ```rust type CodeHash = [u8; 32]; @@ -106,10 +115,15 @@ struct Authorizer { The `code_hash` of the Authorizer is assumed to be the hash of some code accessible in the Relay-chain's Storage pallet. The procedure itself is called the *Authorization Procedure* (`AuthProcedure`) and is expressed in this code (which must be capable of in-core VM execution). Its entry-point prototype is: ```rust -fn is_authorized(param: &AuthParam, package: &WorkPackage) -> bool; +struct Context { + height: BlockNumber, + header_hash: [u8; 32], + core_index: CoreIndex, +} +fn is_authorized(param: &AuthParam, package: &WorkPackage, context: &Context) -> bool; ``` -If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. +If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. @@ -132,7 +146,7 @@ However with this proposal (and even the advent of on-demand parachains), valida This ensures that Validators do a strictly limited amount of work before knowing whether they will be rewarded and are able to discontinue and attempt other candidates earlier than would otherwise be the case. There is the possibility of wasting Coretime by processing Work Packages which result in error, but well-written authorization procedures can mitigate this risk by making a prior validation of the Work Items. -### Collect-Refine +### Refine The `refine` function is implemented as an entry-point inside a code blob which is stored on-chain and whose hash is associated with the Work Class. @@ -146,9 +160,9 @@ type WorkOutput = BoundedVec; fn refine(payload: WorkPayload) -> WorkOutput; ``` -Both `refine` and `is_authorized` are only ever executed on-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. +Both `refine` and `is_authorized` are only ever executed in-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. -Several host functions, largely in line with the *PVF* host functions, are supplied. Two additional ones include: +Several host functions (largely in line with the host functions available to Parachain Validation Function code) are supplied. Two additional ones include: ```rust /// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. @@ -159,53 +173,139 @@ fn state_root(height: u32) -> Option<[u8; 32]>; Other host functions will allow for the possibility of executing a WebAssembly payload (for example, a Parachain Validation Function) or instantiating and entering a subordinate RISCV VM (for example for Actor Progressions). -When applying `refine` from the client code, we must allow for the possibility that the VM exits unexpectedly or does not end. If this happens, then the Work Item is invalidated. Thus we define a type `WorkResult`: +When applying `refine` from the client code, we must allow for the possibility that the VM exits unexpectedly or does not end. Validators are always rewarded for computing properly authorized Work Packages, including those which include such broken Work Items. But they must be able to report their broken state into the Relay-chain in order to collect their reward. Thus we define a type `WorkResult`: ```rust enum WorkError { Timeout, Panic, } -type WorkResult = Result; +struct WorkResult { + class: WorkClass, + item_hash: [u8; 32], + result: Result, + weight: Weight, +} fn apply_refine(item: WorkItem) -> WorkResult; ``` -Each Relay-chain block, every Validator Group representing a Core which is assigned work provides up to one Work Result coherent with the assignment of that Core. Validators are rewarded when they take part in their Group and process a Work Package. This culminates in providing a *Work Result* to the Relay-chain block author. If no Work Result is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. +The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. + +```rust +struct WorkStatement { + latest_relay_block_hash: [u8; 32], + results: BoundedVec, +} +struct Attestation { + statement: WorkStatement, + validator: AccountId, + signature: Signature, +} +``` + +Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Statement* concerning the work done and the results of it. This signed Statement is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. + +The process continues once the Attestations arrive at the Relay-chain Block Author. ### Join-Accumulate -Join-Accumulate is a second stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. +Join-Accumulate is a second major stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. + +Being on-chain (rather than in-core as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out by the block-author and the resultant block evaluated by all validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. + +The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. -The Join-Progess stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions and oversee message queue transitions. +#### Metering -The user-supplied `accumulate` function defines the elective portion of the Join-Accumulate stage. This is executed in a *metered* format, meaning it must be able to be executed in a sandboxed and non-deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. +Join-Accumulate is, as the name suggests, comprised of two subordinate stages. Both stages involve executing code inside a VM on-chain. Thus code must be executed in a *metered* format, meaning it must be able to be executed in a sandboxed and deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. Practically speaking, we may allow a similar VM execution metering system similar to that for the `refine` execution, whereby we do not require a strictly deterministic means of interrupting, but do require deterministic metering and only approximate interruption. This would mean that full-nodes and Relay-chain validators could be made to execute some additional margin worth of computation without payment, though any attack could easily be mitigated by attaching a fixed cost (either economically or in weight terms) to an VM invocation. -The function signature to the `accumulate` entry-point in the Work Class's code blob is: +Each Work Class defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Statement does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. + +```rust +struct WorkItemWeightRequirements { + prune: Weight, + accumulate: Weight, +} +type WeightRequirements = StorageMap; +``` + +Each Work Class has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Work Class logic and represent the amount of weight allotted for each Work Item of this class included in a Work Package assigned to a Core. + +The total amount of weight utilizable by each Work Package (`weight_per_package`) is specified as: + +``` +weight_per_package := relay_block_weight * safety_margin / max_cores +``` + +`safety_margin` ensures that other Relay-chain system processes can happen and important transactions can be processed and is likely to be around 75%. + +A Work Statement is only valid if all weight liabilities of all included Work Items fit within this limit: + +``` +let total_weight_requirement = work_statement + .items + .map(|item| weight_requirements[item.class]) + .sum(|requirements| requirements.prune + requirements.accumulate) +total_weight_requirement <= weight_per_package +``` + +Because of this, Work Statement builders must be aware of any upcoming alterations to `max_cores` and build Statements which are in accordance with it not at present but also in the near future when it may have changed. + +#### Join and `prune` + +_For consideration: Place a hard limit on total weight able to be used by `prune` in any Work Package since it is normally computed twice and an attacker can force it to be computed a third time._ + +The main difference between code called in the Join stage and that in the Accumulate stage is that in the former code is required to exit successfully, within the weight limit and may not mutate any state. + +The Join stage involves the Relay-chain Block Author gathering together all Work Packages backed by a Validator Group in a manner similar to the current system of PoV candidates. The Work Results are grouped according to their Work Class, and the untrusted Work Class function `prune` is called once for each such group. This returns a `Vec` of invalid indices. Using this result, invalid Work Packages may be pruned and the resultant set retried with confidence that the result will be an empty `Vec`. + +```rust +fn prune(outputs: Vec) -> Vec; +``` + +The call to the `prune` function is allocated weight equal to the length of `outputs` multiplied by the `prune` field of the Work Class's weight requirements. + +The `prune` function is used by the Relay-chain Block Author in order to ensure that all Work Packages which make it through the Join stage are non-conflicting and valid for the present Relay-chain state. All Work Packages are rechecked using the same procedure on-chain and the block is considered malformed (i.e. it panics) if the result is not an empty `Vec`. + +The `prune` function has immutable access to the Work Class's child trie state, as well as regular read-only storage access to the Relay-chain's wider state. + +```rust +fn get_work_storage(key: &[u8]) -> Result>; +fn get_work_storage_len(key: &[u8]); +``` + +The amount of weight `prune` is allowed to use is a fixed multiple of the number of + +#### Accumulate + +The second stage is that of Accumulate. The function signature to the `accumulate` entry-point in the Work Class's code blob is: ```rust fn accumulate(results: Vec<(Authorization, Vec<(ItemHash, WorkResult)>)>); type ItemHash = [u8; 32]; ``` -As stated, there is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. This is the sum of all `progressions` fields whose first item is the particular Work Class across all scheduled Work Packages. +As stated, there is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Work Class's weight requirements. + +However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Statement on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by classes within one package may be carried over to the next package and utilized there. Work Items are identified by their hash (`ItemHash`). We provide both the authorization of the package and the item identifers and their results in order to allow the `refine` logic to take appropriate action in the case that an invalid Work Item was issued. _(Note for later: We may wish to provide a more light-client friendly Work Item identifier than a simple hash; perhaps a Merkle root of equal-size segments.)_ -Read-access to the entire Relay-chain state is allowed, but no direct write access may be provided since `refine` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. - ```rust -fn checkpoint() -> Weight; -fn weight_remaining() -> Weight; fn get_work_storage(key: &[u8]) -> Result>; fn get_work_storage_len(key: &[u8]); +fn checkpoint() -> Weight; +fn weight_remaining() -> Weight; fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; fn remove_work_storage(key: &[u8]); ``` +Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `refine` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. + Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `refine` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). @@ -321,21 +421,25 @@ The proposal introduces no new privacy concerns. ## Future Directions and Related Material -Important considerations include: - -1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the subpackages. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Work Class State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). +None at present. ## Drawbacks, Alternatives and Unknowns -None at present. +Important considerations include: -## Prior Art and References +1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the Work Items. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Work Class State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). -None. +2. Work Items may need some degree of coordination to be useful by the `accumulate` function of their Work Class. To a large extent this is outside of the scope of this proposal's computation model by design. Through the authorization framework we assert that it is the concern of the Work Class and not of the Relay-chain validators themselves. However we must ensure that certain requirements of the parachains use-case are practically fulfillable in *some* way. Within the legacy parachain model, PoVs: + 1. shouldn't be replayable; + 2. shouldn't require unbounded buffering in `accumulate` if things are submitted out-of-order; + 3. should be possible to evaluate for ordering by validators making a best-effort. +## Prior Art and References +None. # Chat +``` for this we need a pallet on the RC to allow arbitrary data to be stored for a deposit, with a safeguard that it would remain in RC state for at least 24 hours (in case of dispute); and a host function to allow the PoV to reference it. this issue is that for fast-changing data, you'd need to store all of the different images for 24 hours each. this would quickly get prohibitive. @@ -525,4 +629,5 @@ This would also quite help for stuff like "elastic scaling" etc. basically, just turn polkadot into a global, secure map-reduce computer. We could just experiment As a new WT -yup \ No newline at end of file +yup +``` \ No newline at end of file From 4384db41e67f0f3f7ec23e3b429ad0891a26aae1 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 25 Sep 2023 15:12:06 +0100 Subject: [PATCH 03/47] Prerequisites, initial checks, migration, a few other bits. --- text/corejam.md | 111 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 92 insertions(+), 19 deletions(-) diff --git a/text/corejam.md b/text/corejam.md index 04124b228..34f577323 100644 --- a/text/corejam.md +++ b/text/corejam.md @@ -52,8 +52,7 @@ In order of importance: The idea of *Proof-of-Validity* and *Parachain Validation Function* as first-class concepts in the Polkadot protocol is removed. These are now specializations of more general concepts. -We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Class Trie*. - +We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Package Output*, *Work Package Result*, *Work Package Report* (also known as a *Candidate*) and *Work Package Attestation*, *Work Class Trie*. ```rust mod v0 { @@ -65,8 +64,22 @@ mod v0 { payload: WorkPayload, } type MaxWorkItemsInPackage = ConstU32<16>; + type MaxWorkPackageDependencies = ConstU32<8>; + enum Authorization { + Instantaneous(InstantaneousAuth), + Bulk(Vec), + } + type HeaderHash = [u8; 32]; + /// Just a Blake2-256 hash of an EncodedWorkPackage. + type WorkPackageHash = [u8; 32]; + type Prerequisites = BoundedVec; + struct Context { + header_hash: HeaderHash, + prerequisites: Prerequisites, + } struct WorkPackage { - authorization: Vec, + authorization: Authorization, + context: Context, items: BoundedVec, } } @@ -86,7 +99,7 @@ impl TryFrom for v0::WorkPackage { } ``` -A *Work Package* is an *Authorization* together with a series of *Work Items*, limited in plurality, versioned and with a maximum encoded size. +A *Work Package* is an *Authorization* together with a series of *Work Items* and a context , limited in plurality, versioned and with a maximum encoded size. Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). @@ -98,7 +111,13 @@ The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect #### Collection and `is_authorized` -Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a pre-requisite for a Work Package to be included included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. +Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. + +There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. + +RCVGs run the risk of a credit owner not having the credit at the point of inclusion, in which case the RCVG will not be rewarded. Credit may never be withdrawn, therefore RCVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RCVG currently may be. + +An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RCVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. An *Authorizer* is a parameterized procedure: @@ -115,12 +134,7 @@ struct Authorizer { The `code_hash` of the Authorizer is assumed to be the hash of some code accessible in the Relay-chain's Storage pallet. The procedure itself is called the *Authorization Procedure* (`AuthProcedure`) and is expressed in this code (which must be capable of in-core VM execution). Its entry-point prototype is: ```rust -struct Context { - height: BlockNumber, - header_hash: [u8; 32], - core_index: CoreIndex, -} -fn is_authorized(param: &AuthParam, package: &WorkPackage, context: &Context) -> bool; +fn is_authorized(param: &AuthParam, package: &WorkPackage, core_index: CoreIndex) -> bool; ``` If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. @@ -192,18 +206,32 @@ fn apply_refine(item: WorkItem) -> WorkResult; The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. ```rust -struct WorkStatement { - latest_relay_block_hash: [u8; 32], +struct WorkReport { + /// The hash of the underlying WorkPackage. + hash: WorkPackageHash, + /// The context of the underlying WorkPackage. + context: Context, + /// The core index of the attesting RCVG. + core_index: CoreIndex, + /// The results of the evaluation of the Items in the underlying Work Package. results: BoundedVec, } struct Attestation { - statement: WorkStatement, + report: WorkReport, + validator: AccountId, + signature: Signature, +} +/// Since all RCVG members should be attesting to the same few Work Reports, it may +/// make sense to send Attestations without the full underlying WorkReport, but only +/// its hash. +struct BareAttestation { + report_hash: WorkReportHash, validator: AccountId, signature: Signature, } ``` -Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Statement* concerning the work done and the results of it. This signed Statement is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. +Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. The process continues once the Attestations arrive at the Relay-chain Block Author. @@ -215,13 +243,46 @@ Being on-chain (rather than in-core as with Collect-Refine), information and com The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. +#### Initial Validation + +There are a number of initial validation requirements which the RCBA must do in order to ensure no time is wasted on further, possibly costly, computation. + +Firstly, any given Work Report must have enough attestation signatures to be considered for inclusion on-chain. Only one Work Report may be considered for inclusion from each RCVG per block. + +Secondly, any Work Reports introduced by the RCBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RCBA head and whose height is less than `RECENT_BLOCKS` from the block which the RCBA is now authoring. + +```rust +const RECENT_BLOCKS: u32 = 16; +``` + +Thirdly, the RCBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. + +Fourthly, the RCBA may not include Work Reports whose prerequisites are not themselves included in *Recent* blocks. + +In order to ensure all of the above tests are honoured by the RCBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RCBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were included in the *Recent* blocks: + +```rust +const MAX_CORES: u32 = 512; +/// Must be ordered. +type InclusionSet = BoundedVec>; +type RecentInclusions = StorageValue>> +``` + +The RCBA must keep an up to date set of which Work Packages have already been included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisites have not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy prerequisites of Work Packages introduced later. + +While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RCBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: + +1. it stops being *Recent*; +2. it becomes included on-chain; or +3. some other Attestation of the same Work Package becomes included on-chain. + #### Metering Join-Accumulate is, as the name suggests, comprised of two subordinate stages. Both stages involve executing code inside a VM on-chain. Thus code must be executed in a *metered* format, meaning it must be able to be executed in a sandboxed and deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. Practically speaking, we may allow a similar VM execution metering system similar to that for the `refine` execution, whereby we do not require a strictly deterministic means of interrupting, but do require deterministic metering and only approximate interruption. This would mean that full-nodes and Relay-chain validators could be made to execute some additional margin worth of computation without payment, though any attack could easily be mitigated by attaching a fixed cost (either economically or in weight terms) to an VM invocation. -Each Work Class defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Statement does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. +Each Work Class defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Report does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. ```rust struct WorkItemWeightRequirements { @@ -241,7 +302,7 @@ weight_per_package := relay_block_weight * safety_margin / max_cores `safety_margin` ensures that other Relay-chain system processes can happen and important transactions can be processed and is likely to be around 75%. -A Work Statement is only valid if all weight liabilities of all included Work Items fit within this limit: +A Work Report is only valid if all weight liabilities of all included Work Items fit within this limit: ``` let total_weight_requirement = work_statement @@ -251,7 +312,7 @@ let total_weight_requirement = work_statement total_weight_requirement <= weight_per_package ``` -Because of this, Work Statement builders must be aware of any upcoming alterations to `max_cores` and build Statements which are in accordance with it not at present but also in the near future when it may have changed. +Because of this, Work Report builders must be aware of any upcoming alterations to `max_cores` and build Statements which are in accordance with it not at present but also in the near future when it may have changed. #### Join and `prune` @@ -289,7 +350,7 @@ type ItemHash = [u8; 32]; As stated, there is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Work Class's weight requirements. -However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Statement on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by classes within one package may be carried over to the next package and utilized there. +However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by classes within one package may be carried over to the next package and utilized there. Work Items are identified by their hash (`ItemHash`). We provide both the authorization of the package and the item identifers and their results in order to allow the `refine` logic to take appropriate action in the case that an invalid Work Item was issued. @@ -407,6 +468,18 @@ We should consider utilizing the Storage Pallet for Parachain Code and store onl Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Work Class Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Work Class Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. +### Notes on Implementation Order + +In order to ease the migration process from the current Polkadot on- and off-chain logic to this proposal, we can envision a partial implementation, or refactoring, which would facilitate the eventual proposal whilst remaining compatible with the pre-existing usage and avoid altering substantial code. + +We therefore envision an initial version of this proposal with minimal modifications to current code: + +1. Remain with Webassembly rather than RISC-V, both for Work Class logic and the subordinate environments which can be set up from Work Class logic. The introduction of Work Classes is a permissioned action requiring governance intervention. Work Packages will otherwise execute as per the proposal. *Minor changes to the status quo.* +2. Attested Work Packages must finish running in time and not panic. Therefore `WorkResult` must have an `Infallible` error type. If an Attestation is posted for a Work Package which panics or times out, then this is a slashable offence. *No change to the status quo.* +3. There should be full generalization over Work Package contents, as per the proposal. Introduction of Authorizers, `refine`, `prune` and `accumulate`. *Additional code to the status quo.* + +Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Work Classes. + ## Performance, Ergonomics and Compatibility This system must be broadly compatible with our existing Parachain Validation Function/Proof-of-Validity model, however a specific feasibility study into transitioning/migration has yet to be completed. From a0962823df366de40733263d61cfcf4a783b58ef Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 25 Sep 2023 15:48:14 +0100 Subject: [PATCH 04/47] Rename for proposing. --- text/{corejam.md => 0027-corejam.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename text/{corejam.md => 0027-corejam.md} (99%) diff --git a/text/corejam.md b/text/0027-corejam.md similarity index 99% rename from text/corejam.md rename to text/0027-corejam.md index 34f577323..5484031b5 100644 --- a/text/corejam.md +++ b/text/0027-corejam.md @@ -1,4 +1,4 @@ -# RFC-XXXX: "CoreJam" +# RFC-0027: CoreJam | | | | --------------- | ------------------------------------------------------------------------------------------- | From e97e10978bdbd2c9c4a749dfbd8a3947d2c82ca3 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 26 Sep 2023 11:22:20 +0100 Subject: [PATCH 05/47] Typos and clarifications --- text/0027-corejam.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/text/0027-corejam.md b/text/0027-corejam.md index 5484031b5..be5943aea 100644 --- a/text/0027-corejam.md +++ b/text/0027-corejam.md @@ -63,8 +63,8 @@ mod v0 { class: WorkClass, payload: WorkPayload, } - type MaxWorkItemsInPackage = ConstU32<16>; - type MaxWorkPackageDependencies = ConstU32<8>; + type MaxWorkItemsInPackage = ConstU32<4>; + type MaxWorkPackagePrerequisites = ConstU32<4>; enum Authorization { Instantaneous(InstantaneousAuth), Bulk(Vec), @@ -72,7 +72,7 @@ mod v0 { type HeaderHash = [u8; 32]; /// Just a Blake2-256 hash of an EncodedWorkPackage. type WorkPackageHash = [u8; 32]; - type Prerequisites = BoundedVec; + type Prerequisites = BoundedVec; struct Context { header_hash: HeaderHash, prerequisites: Prerequisites, @@ -171,7 +171,13 @@ type ClassCodeHash = StorageMap; ```rust type WorkOutputLen = ConstU32<1024>; type WorkOutput = BoundedVec; -fn refine(payload: WorkPayload) -> WorkOutput; +fn refine( + payload: WorkPayload, + authorization: Authorization, + auth_id: Option, + context: Context, + package_hash: WorkPackageHash, +) -> WorkOutput; ``` Both `refine` and `is_authorized` are only ever executed in-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. @@ -348,6 +354,8 @@ fn accumulate(results: Vec<(Authorization, Vec<(ItemHash, WorkResult)>)>); type ItemHash = [u8; 32]; ``` +The logic in `accumulate` may need to know how the various Work Items arrived into a processed Work Package. Since a Work Package could have multiple Work Items of the same Work Class, it makes sense to have a separate inner `Vec` for Work Items sharing the Authorization (by virtue of being in the same Work Package). + As stated, there is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Work Class's weight requirements. However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by classes within one package may be carried over to the next package and utilized there. @@ -375,7 +383,7 @@ Other host functions, including some to access Relay-chain hosted services such _(Note for discussion: Should we be considering light-client proof size at all here?)_ -We can already imagine two kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0) and *Actor Progression* (as per Coreplay). Given how abstract the model is, one might reasonably expect many more. +We can already imagine three kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0), *Actor Progression* (as per Coreplay in a yet-to-be-proposed RFC) and Simple Ordering (placements of elements into a namespaced Merkle trie). Given how abstract the model is, one might reasonably expect many more. ### Relay-chain Storage Pallet From 7f9aa0bf65a4513260e60319d5d2eceea8474d8f Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 26 Sep 2023 14:40:39 +0100 Subject: [PATCH 06/47] Ordering: Soft and Hard --- text/0027-corejam.md | 70 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/text/0027-corejam.md b/text/0027-corejam.md index be5943aea..f64fc96d4 100644 --- a/text/0027-corejam.md +++ b/text/0027-corejam.md @@ -105,6 +105,52 @@ Work Items are a pair of class and payload, where the `class` identifies the Cla Though this process happens entirely in consensus, there are two main consensus environments at play, _in-core_ and _on-chain_. We therefore partition the progress into two pairs of stages: Collect & Refine and Join & Accumulate. +### Processing stages of a Work Package + +A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. + +While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *scope* for the Work Package, allowing its inclusion only on a small portion of the (yet to be built, presumably) Relay-chain. The Relay-chain height at this point might be `T`. + +The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r`. + +The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that any prerequisites it relies upon have been submitted. This is called the initial *Inclusion* on-chain and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. + +At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. + +#### Soft-Ordered Queueing Alternative + +In this alternative the prerequisites DAG may be arbitrarily complex, but actual ordering is only guaranteed going *into* the Availability Protocol, not at the point of Accumulation. + +The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Initial Inclusion is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. + +It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time out and are discarded from Relay-chain state. + +#### Hard-Ordered Queueing Alternative + +This alternative gives a guarantee that the order in which a Work Package's Items will be Accumulated will respect the stated prerequisites of that Work Package. It is more complex and relies on substantial off-chain logic to inform the on-chain logic about which Work Packages are Accumulatable. + +Firstly, the number of prerequisites of a Work Package is limited to one. We cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupta sequence by introducing their own Work Packages which a prerequisite which is within another's sequence. + +The (on-chain) repercussion of the Availability Protocol completing for the Work Package depends based on whether it has a prerequisite which is still pending Availability. Since we know that all prerequisite Work Packages must have entered the Availability Protocol due to the Initial Validity Checks, if we are no longer tracking the Work Package and its Work Results on-chain we may safely assume that it is because we have Accumulated them and thus that the Work Package is Available. + +If we are still explicitly retaining a record of the prerequisite Work Package and Work Results, we can assume that the availability process for it has not yet concluded or has already failed. + +If Availability has not yet concluded, we append a reference to (i.e. a hash of) the Work Package to a *Queue of Available Dependents* keyyed by the prerequisite Work Package. The bounding for this list may safely be quite large, and if it grows beyond the bound, the Work Results may be discarded. This could only happen if very many Work Packages are produced and processed with the same prerequisite at approximately the same point in time, and that prerequisite suffers delayed Availability yet the dependents do not: an unlikely eventuality. + +In the case that we are not retaining a record of the prerequisite Work Package and Work Results, we enqueue the Work Results for Accumulation. If there is a non-empty Queue of Available Dependents for this Work Package, we record the fact that this Work Package is *Now Accumulated* in a separate storage item (to circumvent a possibly expensive read/write). If there is not, then we do not record this and will effectively stop tracking the Work Package's Availability status on-chain. + +Finally, after all Availability notices have been processed, but before the Accumulation happens, the RcBA may, via an extrinsic, inform the chain of Work Packages which are the prerequisite preventing available Work Packages from being Accumulated. This amounts to naming a Work Package which has both a Queue of Available Dependents and is Now Accumulated. + +If Availability suffers a time-out (and retrying is not an option), or if the prerequisite has suffered a time-out, then all dependent Work Packages must be discarded in a cascading manner, a potentially daunting proposition. In a manner similar to that above, the time-out itself is recorded in on-chain storage item which itself may only be removed after the latest time at which the newest possible prerequisites (given the Initial Validity Checks) could become available. The RcBAs may then introduce extrinsics to remove these associated storage items in an iterative fashion without the Work Results becoming Accumulated. + +#### Discussion + +An initial implementation of this proposal would almost certainly provide only the soft ordering, since it practically a waypoint to get to the hard ordering implementation. + +Initial Validity Checks are made on-chain prior to the Availabilty Protocol begins for any given Work Package. This ensures that the Work Package is still in scope $-$ i.e. recent enough and on the proper fork. However, this does not ensure that the Work Package is still in scope at the point that the Work Results are actually Accumulated. It is as yet unclear whether this is especially problematic. + +The same scope limit could be placed also on Accumulation; in neither variant does it introduce much additional complexity. In both cases it would require the same course of action as of Availability timing out without the possibility of retry. However whereas in the soft-variant we would not expect to see very differeny dynamics since one such time-out has no repurcissions beyond preventing the Accumulation of those Work Results, in the hard-ordering variant, it could mean a substantially greater occurance of the cascading failure logic calling into question the real purpose of the scoping: is it to protect the Validators from having to deal with indefinitely valid Work Packages or is it to protect the Accumulation logic from having to deal with the Results of older Work Packages? + ### Collect-Refine The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these attestations form the basis for inclusion on-chain and integration into the Relay-chain's state (in the following stages). @@ -115,7 +161,7 @@ Collection is the means of a Validator Group member attaining a Work Package whi There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. -RCVGs run the risk of a credit owner not having the credit at the point of inclusion, in which case the RCVG will not be rewarded. Credit may never be withdrawn, therefore RCVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RCVG currently may be. +RCVGs run the risk of a credit owner not having the credit at the point of inclusion, in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RCVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RCVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. @@ -217,7 +263,7 @@ struct WorkReport { hash: WorkPackageHash, /// The context of the underlying WorkPackage. context: Context, - /// The core index of the attesting RCVG. + /// The core index of the attesting RcVG. core_index: CoreIndex, /// The results of the evaluation of the Items in the underlying Work Package. results: BoundedVec, @@ -227,7 +273,7 @@ struct Attestation { validator: AccountId, signature: Signature, } -/// Since all RCVG members should be attesting to the same few Work Reports, it may +/// Since all RcVG members should be attesting to the same few Work Reports, it may /// make sense to send Attestations without the full underlying WorkReport, but only /// its hash. struct BareAttestation { @@ -251,21 +297,21 @@ The Join-Accumulate stage may be seen as a synchronized counterpart to the paral #### Initial Validation -There are a number of initial validation requirements which the RCBA must do in order to ensure no time is wasted on further, possibly costly, computation. +There are a number of initial validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. -Firstly, any given Work Report must have enough attestation signatures to be considered for inclusion on-chain. Only one Work Report may be considered for inclusion from each RCVG per block. +Firstly, any given Work Report must have enough attestation signatures to be considered for inclusion on-chain. Only one Work Report may be considered for inclusion from each RcVG per block. -Secondly, any Work Reports introduced by the RCBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RCBA head and whose height is less than `RECENT_BLOCKS` from the block which the RCBA is now authoring. +Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. ```rust const RECENT_BLOCKS: u32 = 16; ``` -Thirdly, the RCBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. +Thirdly, the RcBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. -Fourthly, the RCBA may not include Work Reports whose prerequisites are not themselves included in *Recent* blocks. +Fourthly, the RcBA may not include Work Reports whose prerequisites are not themselves included in *Recent* blocks. -In order to ensure all of the above tests are honoured by the RCBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RCBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were included in the *Recent* blocks: +In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were included in the *Recent* blocks: ```rust const MAX_CORES: u32 = 512; @@ -274,9 +320,9 @@ type InclusionSet = BoundedVec>; type RecentInclusions = StorageValue>> ``` -The RCBA must keep an up to date set of which Work Packages have already been included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisites have not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy prerequisites of Work Packages introduced later. +The RcBA must keep an up to date set of which Work Packages have already been included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisites have not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy prerequisites of Work Packages introduced later. -While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RCBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: +While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: 1. it stops being *Recent*; 2. it becomes included on-chain; or @@ -486,6 +532,8 @@ We therefore envision an initial version of this proposal with minimal modificat 2. Attested Work Packages must finish running in time and not panic. Therefore `WorkResult` must have an `Infallible` error type. If an Attestation is posted for a Work Package which panics or times out, then this is a slashable offence. *No change to the status quo.* 3. There should be full generalization over Work Package contents, as per the proposal. Introduction of Authorizers, `refine`, `prune` and `accumulate`. *Additional code to the status quo.* +To optimize the present situation, a number of "natively implemented", fixed-function registrations will be provided. The Work Class of index zero will be used to represent the Parachains Work Class and will have a "native" (i.e. within the Wasm runtime) implementation of `refine` and `accumulate`. Secondly, a fixed-function set of Auth IDs 9,999 and lower simply represent Authorizers which accept Work Packages which contain a single Work Item of the Parachains Work Class which pertain to progressing a parachain of ID equal to the Auth ID value. + Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Work Classes. ## Performance, Ergonomics and Compatibility From e4a117b0bc2d8c06a881c60cc8f0ca6655601dce Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 26 Sep 2023 14:47:12 +0100 Subject: [PATCH 07/47] Remove superfluous --- text/0027-corejam.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/text/0027-corejam.md b/text/0027-corejam.md index f64fc96d4..334b489db 100644 --- a/text/0027-corejam.md +++ b/text/0027-corejam.md @@ -389,8 +389,6 @@ fn get_work_storage(key: &[u8]) -> Result>; fn get_work_storage_len(key: &[u8]); ``` -The amount of weight `prune` is allowed to use is a fixed multiple of the number of - #### Accumulate The second stage is that of Accumulate. The function signature to the `accumulate` entry-point in the Work Class's code blob is: From c4f2e0d2a77710c5433bfe32dde51a3eee477084 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 26 Sep 2023 22:31:37 +0100 Subject: [PATCH 08/47] Correct number --- text/0027-corejam.md | 760 ------------------------------------------- 1 file changed, 760 deletions(-) delete mode 100644 text/0027-corejam.md diff --git a/text/0027-corejam.md b/text/0027-corejam.md deleted file mode 100644 index 334b489db..000000000 --- a/text/0027-corejam.md +++ /dev/null @@ -1,760 +0,0 @@ -# RFC-0027: CoreJam - -| | | -| --------------- | ------------------------------------------------------------------------------------------- | -| **Start Date** | 11 September 2023 | -| **Description** | Parallelised, decentralised, permissionless state-machine based on a multistage Collect-Refine-Join-Accumulate model. | -| **Authors** | Gavin Wood, Robert Habermeier, Bastian Köcher | - - -## Summary - -This is a proposal to fundamentally alter the workload done on the Polkadot Relay-chain, both in terms of that which is done "on-chain", i.e. by all Relay Chain Validators (*Validators*) as well as that which is done "in-core", i.e. distributed among subsets of the Validators (*Validator Groups*). The target is to create a model which closely matches the underlying technical architecture and is both generic and permissionlessly extensible. - -In the proposed model, code is stored on-chain with two entry-points. Workloads are collated and processed in-core (and thus parallelized) using one entry-point, whereas the refined outputs of this processing are gathered together and an on-chain state-machine progressed according to the other. - -While somewhat reminiscent of the Map-Reduce paradigm, a comprehensive analogy cannot be taken: the in-core processing code does not transform a set of inputs, but is rather used to refine entirely arbitrary input data collected by some third-party. Instead, and in accordance, we term it *Collect-Refine-Join-Accumulate*. - -## Motivation - -Polkadot was originally designed as a means of validating state transitions of Webassembly-defined state machines known as *Parachain Validation Functions*. These state machines were envisioned to be long-lived (of the order of years) and transitioning continuously, at the "full capacity" of modern single-threaded hardware held in consensus over the internet, and in isolation to any other such state machines. - -Having actually built Polkadot, it became clear that the flexibility of the machinery implementing it allowed for a more diverse set of usage patterns and models. Parathreads, which came to be known as *On-Demand Parachains* (ODP) is one such model. This was underlined by other proposals to allow for a more decentralised administration of how the underlying Polkadot Core resource is procured, in particular *Agile Coretime*. - -More recently, the idea of having small to medium size programs executing without its own surrounding blockchain using only Relay-chain resources has been discussed in detail primarily around the *Coreplay* proposal. It therefore seems short-sighted to assume other models could not exist for utilizing the Relay-chain's "Core" resource. Therefore in much the same way that Agile Coretime originally strived to provide the most general model of *procuring* Relay-chain's Core resource, it seems sensible to strive to find a similarly general model for *utilizing* this resource, one minimizing the difference between the valuable function of the Validators and the service offered by Polkadot. - -Beyond delivering additional value through the increased potential for use-cases that this flexibility allows, our motivation extends to gaining stability: a future-proof platform allowing teams to build on it without fear of high maintenance burden, continuous bitrot or a technological rug-pull at some later date. Secondly, we are motivated by reducing barriers for new teams, allowing the Polkadot platform to harness the power of the crowd which permissionless systems uniquely enable. - -Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the classical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. - -## Requirements - -In order of importance: - -1. The proposal must be compatible, in principle, with the preexisting parachain model. -2. The proposal must facilitate the implementation of Coreplay. -3. The proposal must be compatible with Agile Coretime, as detailed in RFC#0001. -4. Implementation of the proposal should need minimal changes to all production logic. -5. Utilization of Coretime must be accessible. -6. Utilization of Coretime must be permissionless. -7. The nature of Coretime should closely match the nature of resources generated by Polkadot. -8. Minimal opinionation should be introduced over the format, nature and usage of Coretime. - -## Stakeholders - -1. Anyone with exposure to the DOT token economy. -2. Anyone wanting to create decentralised/unstoppable/resilient applications. -3. Teams already building on Polkadot. - -## Explanation - -**CoreJam is a general model for utilization of Polkadot Cores. It is a mechanism by which Work Packages are communicated, authorized, computed and verified, and their results gathered, combined and accumulated into particular parts of the Relay-chain's state.** - -The idea of *Proof-of-Validity* and *Parachain Validation Function* as first-class concepts in the Polkadot protocol is removed. These are now specializations of more general concepts. - -We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Package Output*, *Work Package Result*, *Work Package Report* (also known as a *Candidate*) and *Work Package Attestation*, *Work Class Trie*. - -```rust -mod v0 { - const PROGRESS_WEIGHT_PER_PACKAGE: Weight = MAX_BLOCK_WEIGHT * 3 / 4; - type WorkClass = u32; - type WorkPayload = Vec; - struct WorkItem { - class: WorkClass, - payload: WorkPayload, - } - type MaxWorkItemsInPackage = ConstU32<4>; - type MaxWorkPackagePrerequisites = ConstU32<4>; - enum Authorization { - Instantaneous(InstantaneousAuth), - Bulk(Vec), - } - type HeaderHash = [u8; 32]; - /// Just a Blake2-256 hash of an EncodedWorkPackage. - type WorkPackageHash = [u8; 32]; - type Prerequisites = BoundedVec; - struct Context { - header_hash: HeaderHash, - prerequisites: Prerequisites, - } - struct WorkPackage { - authorization: Authorization, - context: Context, - items: BoundedVec, - } -} -type MaxWorkPackageSize = ConstU32<5 * 1024 * 1024>; -struct EncodedWorkPackage { - version: u32, - encoded: BoundedVec, -} -impl TryFrom for v0::WorkPackage { - type Error = (); - fn try_from(e: EncodedWorkPackage) -> Result { - match e.version { - 0 => Self::decode(&mut &e.encoded[..]).map_err(|_| ()), - _ => Err(()), - } - } -} -``` - -A *Work Package* is an *Authorization* together with a series of *Work Items* and a context , limited in plurality, versioned and with a maximum encoded size. - -Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). - -Though this process happens entirely in consensus, there are two main consensus environments at play, _in-core_ and _on-chain_. We therefore partition the progress into two pairs of stages: Collect & Refine and Join & Accumulate. - -### Processing stages of a Work Package - -A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. - -While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *scope* for the Work Package, allowing its inclusion only on a small portion of the (yet to be built, presumably) Relay-chain. The Relay-chain height at this point might be `T`. - -The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r`. - -The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that any prerequisites it relies upon have been submitted. This is called the initial *Inclusion* on-chain and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. - -At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. - -#### Soft-Ordered Queueing Alternative - -In this alternative the prerequisites DAG may be arbitrarily complex, but actual ordering is only guaranteed going *into* the Availability Protocol, not at the point of Accumulation. - -The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Initial Inclusion is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. - -It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time out and are discarded from Relay-chain state. - -#### Hard-Ordered Queueing Alternative - -This alternative gives a guarantee that the order in which a Work Package's Items will be Accumulated will respect the stated prerequisites of that Work Package. It is more complex and relies on substantial off-chain logic to inform the on-chain logic about which Work Packages are Accumulatable. - -Firstly, the number of prerequisites of a Work Package is limited to one. We cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupta sequence by introducing their own Work Packages which a prerequisite which is within another's sequence. - -The (on-chain) repercussion of the Availability Protocol completing for the Work Package depends based on whether it has a prerequisite which is still pending Availability. Since we know that all prerequisite Work Packages must have entered the Availability Protocol due to the Initial Validity Checks, if we are no longer tracking the Work Package and its Work Results on-chain we may safely assume that it is because we have Accumulated them and thus that the Work Package is Available. - -If we are still explicitly retaining a record of the prerequisite Work Package and Work Results, we can assume that the availability process for it has not yet concluded or has already failed. - -If Availability has not yet concluded, we append a reference to (i.e. a hash of) the Work Package to a *Queue of Available Dependents* keyyed by the prerequisite Work Package. The bounding for this list may safely be quite large, and if it grows beyond the bound, the Work Results may be discarded. This could only happen if very many Work Packages are produced and processed with the same prerequisite at approximately the same point in time, and that prerequisite suffers delayed Availability yet the dependents do not: an unlikely eventuality. - -In the case that we are not retaining a record of the prerequisite Work Package and Work Results, we enqueue the Work Results for Accumulation. If there is a non-empty Queue of Available Dependents for this Work Package, we record the fact that this Work Package is *Now Accumulated* in a separate storage item (to circumvent a possibly expensive read/write). If there is not, then we do not record this and will effectively stop tracking the Work Package's Availability status on-chain. - -Finally, after all Availability notices have been processed, but before the Accumulation happens, the RcBA may, via an extrinsic, inform the chain of Work Packages which are the prerequisite preventing available Work Packages from being Accumulated. This amounts to naming a Work Package which has both a Queue of Available Dependents and is Now Accumulated. - -If Availability suffers a time-out (and retrying is not an option), or if the prerequisite has suffered a time-out, then all dependent Work Packages must be discarded in a cascading manner, a potentially daunting proposition. In a manner similar to that above, the time-out itself is recorded in on-chain storage item which itself may only be removed after the latest time at which the newest possible prerequisites (given the Initial Validity Checks) could become available. The RcBAs may then introduce extrinsics to remove these associated storage items in an iterative fashion without the Work Results becoming Accumulated. - -#### Discussion - -An initial implementation of this proposal would almost certainly provide only the soft ordering, since it practically a waypoint to get to the hard ordering implementation. - -Initial Validity Checks are made on-chain prior to the Availabilty Protocol begins for any given Work Package. This ensures that the Work Package is still in scope $-$ i.e. recent enough and on the proper fork. However, this does not ensure that the Work Package is still in scope at the point that the Work Results are actually Accumulated. It is as yet unclear whether this is especially problematic. - -The same scope limit could be placed also on Accumulation; in neither variant does it introduce much additional complexity. In both cases it would require the same course of action as of Availability timing out without the possibility of retry. However whereas in the soft-variant we would not expect to see very differeny dynamics since one such time-out has no repurcissions beyond preventing the Accumulation of those Work Results, in the hard-ordering variant, it could mean a substantially greater occurance of the cascading failure logic calling into question the real purpose of the scoping: is it to protect the Validators from having to deal with indefinitely valid Work Packages or is it to protect the Accumulation logic from having to deal with the Results of older Work Packages? - -### Collect-Refine - -The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these attestations form the basis for inclusion on-chain and integration into the Relay-chain's state (in the following stages). - -#### Collection and `is_authorized` - -Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. - -There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. - -RCVGs run the risk of a credit owner not having the credit at the point of inclusion, in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RCVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. - -An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RCVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. - -An *Authorizer* is a parameterized procedure: - -```rust -type CodeHash = [u8; 32]; -type AuthParamSize = ConstU32<1024>; -type AuthParam = BoundedVec; -struct Authorizer { - code_hash: CodeHash, - param: AuthParam, -} -``` - -The `code_hash` of the Authorizer is assumed to be the hash of some code accessible in the Relay-chain's Storage pallet. The procedure itself is called the *Authorization Procedure* (`AuthProcedure`) and is expressed in this code (which must be capable of in-core VM execution). Its entry-point prototype is: - -```rust -fn is_authorized(param: &AuthParam, package: &WorkPackage, core_index: CoreIndex) -> bool; -``` - -If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. - -A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. - -Since encoded `Authorizer` values may be fairly large (up to 1,038 bytes here), they may not be a drop-in replacement for the `ParaId`/`TaskId` used at present in the Agile Coretime interface. Because of this, we provide a lookup mechanism allowing a much shorter `AuthId` to be used within the Coretime scheduling messaging. Conveniently, this is precisely the same datatype size (32-bit) as a `ParaId`/`TaskId`. - -There is an Authorizations Pallet which stores the association. Adding a new piece of code is permissionless but requires a deposit commensurate with its size. - -```rust -type AuthId = u32; -type Authorizers = StorageMap; -``` - -An *Authorization* is simply a blob which helps the Authorizer recognize a properly authorized Work Package. No constraints are placed on Authorizers over how they may interpret this blob. Expected authorization content includes signatures, Merkle-proofs and more exotic succinct zero-knowledge proofs. - -_(Note: depending on future Relay-chain Coretime scheduling implementation concerns, a window of Relay-chain blocks)._ - -The need of validators to be rewarded for doing work they might reasonably expect to be useful competes with that of the Coretime procurers to be certain to get work done which is useful to them. In Polkadot 1.0, validators only get rewarded for PoVs ("work packages") which do not panic or overrun. This ensures that validators are well-incentivized to ensure that their computation is useful for the assigned parachain. This incentive model works adequately where all PVF code is of high quality and collators are few and static. - -However with this proposal (and even the advent of on-demand parachains), validators have little ability to identify a high-quality Work Package builder and the permissionless design means a greater expectation of flawed code executing in-core. Because of this, we make a slightly modified approach: Work Packages must have a valid Authorization, i.e. the Coretime-assigned `is_authorized` returns `true` when provided with the Work Package. However, Validators get rewarded for *any* such authorized Work Package, even one which ultimately panics or overruns on its evaluation. - -This ensures that Validators do a strictly limited amount of work before knowing whether they will be rewarded and are able to discontinue and attempt other candidates earlier than would otherwise be the case. There is the possibility of wasting Coretime by processing Work Packages which result in error, but well-written authorization procedures can mitigate this risk by making a prior validation of the Work Items. - -### Refine - -The `refine` function is implemented as an entry-point inside a code blob which is stored on-chain and whose hash is associated with the Work Class. - -```rust -type ClassCodeHash = StorageMap; -``` - -```rust -type WorkOutputLen = ConstU32<1024>; -type WorkOutput = BoundedVec; -fn refine( - payload: WorkPayload, - authorization: Authorization, - auth_id: Option, - context: Context, - package_hash: WorkPackageHash, -) -> WorkOutput; -``` - -Both `refine` and `is_authorized` are only ever executed in-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. - -Several host functions (largely in line with the host functions available to Parachain Validation Function code) are supplied. Two additional ones include: - -```rust -/// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. -fn lookup(hash: [u8; 32]) -> Vec; -/// Determine the state root of the block at given `height`. -fn state_root(height: u32) -> Option<[u8; 32]>; -``` - -Other host functions will allow for the possibility of executing a WebAssembly payload (for example, a Parachain Validation Function) or instantiating and entering a subordinate RISCV VM (for example for Actor Progressions). - -When applying `refine` from the client code, we must allow for the possibility that the VM exits unexpectedly or does not end. Validators are always rewarded for computing properly authorized Work Packages, including those which include such broken Work Items. But they must be able to report their broken state into the Relay-chain in order to collect their reward. Thus we define a type `WorkResult`: - -```rust -enum WorkError { - Timeout, - Panic, -} -struct WorkResult { - class: WorkClass, - item_hash: [u8; 32], - result: Result, - weight: Weight, -} -fn apply_refine(item: WorkItem) -> WorkResult; -``` - -The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. - -```rust -struct WorkReport { - /// The hash of the underlying WorkPackage. - hash: WorkPackageHash, - /// The context of the underlying WorkPackage. - context: Context, - /// The core index of the attesting RcVG. - core_index: CoreIndex, - /// The results of the evaluation of the Items in the underlying Work Package. - results: BoundedVec, -} -struct Attestation { - report: WorkReport, - validator: AccountId, - signature: Signature, -} -/// Since all RcVG members should be attesting to the same few Work Reports, it may -/// make sense to send Attestations without the full underlying WorkReport, but only -/// its hash. -struct BareAttestation { - report_hash: WorkReportHash, - validator: AccountId, - signature: Signature, -} -``` - -Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. - -The process continues once the Attestations arrive at the Relay-chain Block Author. - -### Join-Accumulate - -Join-Accumulate is a second major stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. - -Being on-chain (rather than in-core as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out by the block-author and the resultant block evaluated by all validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. - -The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. - -#### Initial Validation - -There are a number of initial validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. - -Firstly, any given Work Report must have enough attestation signatures to be considered for inclusion on-chain. Only one Work Report may be considered for inclusion from each RcVG per block. - -Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. - -```rust -const RECENT_BLOCKS: u32 = 16; -``` - -Thirdly, the RcBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. - -Fourthly, the RcBA may not include Work Reports whose prerequisites are not themselves included in *Recent* blocks. - -In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were included in the *Recent* blocks: - -```rust -const MAX_CORES: u32 = 512; -/// Must be ordered. -type InclusionSet = BoundedVec>; -type RecentInclusions = StorageValue>> -``` - -The RcBA must keep an up to date set of which Work Packages have already been included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisites have not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy prerequisites of Work Packages introduced later. - -While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: - -1. it stops being *Recent*; -2. it becomes included on-chain; or -3. some other Attestation of the same Work Package becomes included on-chain. - -#### Metering - -Join-Accumulate is, as the name suggests, comprised of two subordinate stages. Both stages involve executing code inside a VM on-chain. Thus code must be executed in a *metered* format, meaning it must be able to be executed in a sandboxed and deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. - -Practically speaking, we may allow a similar VM execution metering system similar to that for the `refine` execution, whereby we do not require a strictly deterministic means of interrupting, but do require deterministic metering and only approximate interruption. This would mean that full-nodes and Relay-chain validators could be made to execute some additional margin worth of computation without payment, though any attack could easily be mitigated by attaching a fixed cost (either economically or in weight terms) to an VM invocation. - -Each Work Class defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Report does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. - -```rust -struct WorkItemWeightRequirements { - prune: Weight, - accumulate: Weight, -} -type WeightRequirements = StorageMap; -``` - -Each Work Class has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Work Class logic and represent the amount of weight allotted for each Work Item of this class included in a Work Package assigned to a Core. - -The total amount of weight utilizable by each Work Package (`weight_per_package`) is specified as: - -``` -weight_per_package := relay_block_weight * safety_margin / max_cores -``` - -`safety_margin` ensures that other Relay-chain system processes can happen and important transactions can be processed and is likely to be around 75%. - -A Work Report is only valid if all weight liabilities of all included Work Items fit within this limit: - -``` -let total_weight_requirement = work_statement - .items - .map(|item| weight_requirements[item.class]) - .sum(|requirements| requirements.prune + requirements.accumulate) -total_weight_requirement <= weight_per_package -``` - -Because of this, Work Report builders must be aware of any upcoming alterations to `max_cores` and build Statements which are in accordance with it not at present but also in the near future when it may have changed. - -#### Join and `prune` - -_For consideration: Place a hard limit on total weight able to be used by `prune` in any Work Package since it is normally computed twice and an attacker can force it to be computed a third time._ - -The main difference between code called in the Join stage and that in the Accumulate stage is that in the former code is required to exit successfully, within the weight limit and may not mutate any state. - -The Join stage involves the Relay-chain Block Author gathering together all Work Packages backed by a Validator Group in a manner similar to the current system of PoV candidates. The Work Results are grouped according to their Work Class, and the untrusted Work Class function `prune` is called once for each such group. This returns a `Vec` of invalid indices. Using this result, invalid Work Packages may be pruned and the resultant set retried with confidence that the result will be an empty `Vec`. - -```rust -fn prune(outputs: Vec) -> Vec; -``` - -The call to the `prune` function is allocated weight equal to the length of `outputs` multiplied by the `prune` field of the Work Class's weight requirements. - -The `prune` function is used by the Relay-chain Block Author in order to ensure that all Work Packages which make it through the Join stage are non-conflicting and valid for the present Relay-chain state. All Work Packages are rechecked using the same procedure on-chain and the block is considered malformed (i.e. it panics) if the result is not an empty `Vec`. - -The `prune` function has immutable access to the Work Class's child trie state, as well as regular read-only storage access to the Relay-chain's wider state. - -```rust -fn get_work_storage(key: &[u8]) -> Result>; -fn get_work_storage_len(key: &[u8]); -``` - -#### Accumulate - -The second stage is that of Accumulate. The function signature to the `accumulate` entry-point in the Work Class's code blob is: - -```rust -fn accumulate(results: Vec<(Authorization, Vec<(ItemHash, WorkResult)>)>); -type ItemHash = [u8; 32]; -``` - -The logic in `accumulate` may need to know how the various Work Items arrived into a processed Work Package. Since a Work Package could have multiple Work Items of the same Work Class, it makes sense to have a separate inner `Vec` for Work Items sharing the Authorization (by virtue of being in the same Work Package). - -As stated, there is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Work Class's weight requirements. - -However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by classes within one package may be carried over to the next package and utilized there. - -Work Items are identified by their hash (`ItemHash`). We provide both the authorization of the package and the item identifers and their results in order to allow the `refine` logic to take appropriate action in the case that an invalid Work Item was issued. - -_(Note for later: We may wish to provide a more light-client friendly Work Item identifier than a simple hash; perhaps a Merkle root of equal-size segments.)_ - -```rust -fn get_work_storage(key: &[u8]) -> Result>; -fn get_work_storage_len(key: &[u8]); -fn checkpoint() -> Weight; -fn weight_remaining() -> Weight; -fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; -fn remove_work_storage(key: &[u8]); -``` - -Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `refine` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. - -Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `refine` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. - -Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). - -Other host functions, including some to access Relay-chain hosted services such as the Balances and Storage Pallet may also be provided commensurate with this executing on-chain. - -_(Note for discussion: Should we be considering light-client proof size at all here?)_ - -We can already imagine three kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0), *Actor Progression* (as per Coreplay in a yet-to-be-proposed RFC) and Simple Ordering (placements of elements into a namespaced Merkle trie). Given how abstract the model is, one might reasonably expect many more. - -### Relay-chain Storage Pallet - -There is a general need to be able to reference large, immutable and long-term data payloads both on-chain and in-core. This is both the case for fixed-function logic such as fetching the VM code for `refine` and `accumulate` as well as from within Work Packages themselves. - -Owing to the potential for forks and disputes to happen beyond the scope of initial validation, there are certain quite subtle requirements over what data held on-chain may be utilized in-core. Because of this, it makes sense to have a general solution which is known to be safe to use in all circumstances. We call this solution the *Storage Pallet*. - -The Storage Pallet provides a simple API, accessible to untrusted code through host functions & extrinsics and to trusted Relay-chain code via a trait interface. - -```rust -trait Storage { - /// Immutable function to attempt to determine the preimage for the given `hash`. - fn lookup(hash: &[u8; 32]) -> Option>; - - /// Allow a particular preimage to be `provide`d. - /// Once provided, this will be available through `lookup` until - /// `unrequest` is called. - fn request(hash: &[u8; 32], len: usize) -> bool; - /// Remove request that some data be made available. If the data was never - /// available or the data will remain available due to another request, - /// then `false` is returned and `expunge` may be called immediately. - /// Otherwise, `true` is returned and `expunge` may be called in - /// 24 hours. - fn unrequest(hash: &[u8; 32]) -> bool; - - // Functions used by implementations of untrusted functions; such as - // extrinsics or host functions. - - /// Place a deposit in order to allow a particular preimage to be `provide`d. - /// Once provided, this will be available through `lookup` until - /// `unrequest_untrusted` is called. - fn request_untrusted(depositor: &AccountId, hash: &[u8; 32], len: usize); - /// Remove request that some data be made available. If the data was never - /// available or the data will remain available due to another request, - /// then `false` is returned and `expunge_untrusted` may be called immediately. - /// Otherwise, `true` is returned and `expunge_untrusted` may be called in - /// 24 hours. - fn unrequest_untrusted(depositor: &AccountId, hash: &[u8; 32]) -> bool; - - // Permissionless items utilizable directly by an extrinsic or task. - - /// Provide the preimage of some requested hash. Returns `Some` if its hash - /// was requested; `None` otherwise. - /// - /// Usually utilized by an extrinsic and is free if `Some` is returned. - fn provide(preimage: &[u8]) -> Option<[u8; 32]>; - /// Potentially remove the preimage of `hash` from the chain when it was - /// unrequested using `unrequest`. `Ok` is returned iff the operation is - /// valid. - /// - /// Usually utilized by a task and is free if it returns `Ok`. - fn expunge(hash: &[u8; 32]) -> Result<(), ()>; - /// Return the deposit associated with the removal of the request by - /// `depositor` using `unrequest_untrusted`. Potentially - /// remove the preimage of `hash` from the chain also. `Ok` is returned - /// iff the operation is valid. - /// - /// Usually utilized by a task and is free if it returns `Ok`. - fn expunge_untrusted(depositor: &AccountId, hash: &[u8; 32]) -> Result<(), ()>; - - /// Equivalent to `request` followed immediately by `provide`. - fn store(data: &[u8]) -> [u8; 32]; -} -``` - -Internally, data is stored with a reference count so that two separate usages of `store` need not be concerned about the other. - -Every piece of data stored for an untrusted caller requires a sizeable deposit. When used by untrusted code via a host function, the `depositor` would be set to an account controlled by the executing code (e.g. the Work Class's sovereign account). - -Removing data happens in a two-phase procedure; first the data is unrequested, signalling that calling `lookup` on its hash may no longer work (it may still work if there are other -requests active). 24 hours following this, the data is expunged with a second call which, actually removes the data from the chain assuming no other requests for it are active. - -Only once expunge is called successfuly is the deposit returned. If the data was never provided, or is additional requests are still active, then expunge may be called immediately after a successful unrequest. - -### Notes on Agile Coretime - -Crucially, a *Task* is no longer a first-class concept. Thus the Agile Coretime model, which in large part allows Coretime to be assigned to a Task Identifier from the Coretime chain, would need to be modified to avoid a hard dependency on this. - -In this proposal, we replace the concept of a Task with a more general ticketing system; Coretime is assigned to an *Authorizer* instead, a parameterized function. This would allow a succinct *Authorization* (i.e. a small blob of data) to be included in the Work Package which, when fed into the relevant Authorizer function could verify that some Work Package is indeed allowed to utilize that Core at (roughly) that time. A simple proof system would be a regular PKI signature. More complex proof systems could include more exotic cryptography (e.g. multisignatures or zk-SNARKs). - -In this model, we would expect any authorized Work Packages which panic or overrun to result in a punishment to the specific author by the logic of the Work Class. - -### Notes for migrating from a Parachain-centric model - -All Parachain-specific data held on the Relay-chain including the means of tracking the Head Data and Code would be held in the Parachains Work Class (Child) Trie. The Work Package would be essentially equivalent to the current PoV blob, though prefixed by the Work Class. `refine` would prove the validity of the parachain transition described in the PoV which is the Work Package. The Parachains Work Output would provide the basis for the input of what is currently termed the Paras Inherent. `accumulate` would identify and resolve any colliding transitions and manage message queue heads, much the same as the current hard-coded logic of the Relay-chain. - -We should consider utilizing the Storage Pallet for Parachain Code and store only a hash in the Parachains Work Class Trie. - -### Notes for implementing the Actor Progression model - -Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Work Class Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Work Class Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. - -### Notes on Implementation Order - -In order to ease the migration process from the current Polkadot on- and off-chain logic to this proposal, we can envision a partial implementation, or refactoring, which would facilitate the eventual proposal whilst remaining compatible with the pre-existing usage and avoid altering substantial code. - -We therefore envision an initial version of this proposal with minimal modifications to current code: - -1. Remain with Webassembly rather than RISC-V, both for Work Class logic and the subordinate environments which can be set up from Work Class logic. The introduction of Work Classes is a permissioned action requiring governance intervention. Work Packages will otherwise execute as per the proposal. *Minor changes to the status quo.* -2. Attested Work Packages must finish running in time and not panic. Therefore `WorkResult` must have an `Infallible` error type. If an Attestation is posted for a Work Package which panics or times out, then this is a slashable offence. *No change to the status quo.* -3. There should be full generalization over Work Package contents, as per the proposal. Introduction of Authorizers, `refine`, `prune` and `accumulate`. *Additional code to the status quo.* - -To optimize the present situation, a number of "natively implemented", fixed-function registrations will be provided. The Work Class of index zero will be used to represent the Parachains Work Class and will have a "native" (i.e. within the Wasm runtime) implementation of `refine` and `accumulate`. Secondly, a fixed-function set of Auth IDs 9,999 and lower simply represent Authorizers which accept Work Packages which contain a single Work Item of the Parachains Work Class which pertain to progressing a parachain of ID equal to the Auth ID value. - -Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Work Classes. - -## Performance, Ergonomics and Compatibility - -This system must be broadly compatible with our existing Parachain Validation Function/Proof-of-Validity model, however a specific feasibility study into transitioning/migration has yet to be completed. - -To aid swift deployment, the Relay-chain may retain its existing parachain-specific logic "hardcoded", and the Coregap logic added separately, with Work Class "zero" being special-cased to mean "the hard-coded Parachain logic". - -## Testing, Security and Privacy - -Standard Polkadot testing and security auditing applies. - -The proposal introduces no new privacy concerns. - -## Future Directions and Related Material - -None at present. - -## Drawbacks, Alternatives and Unknowns - -Important considerations include: - -1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the Work Items. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Work Class State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). - -2. Work Items may need some degree of coordination to be useful by the `accumulate` function of their Work Class. To a large extent this is outside of the scope of this proposal's computation model by design. Through the authorization framework we assert that it is the concern of the Work Class and not of the Relay-chain validators themselves. However we must ensure that certain requirements of the parachains use-case are practically fulfillable in *some* way. Within the legacy parachain model, PoVs: - 1. shouldn't be replayable; - 2. shouldn't require unbounded buffering in `accumulate` if things are submitted out-of-order; - 3. should be possible to evaluate for ordering by validators making a best-effort. - -## Prior Art and References - -None. - -# Chat -``` -for this we need a pallet on the RC to allow arbitrary data to be stored for a deposit, with a safeguard that it would remain in RC state for at least 24 hours (in case of dispute); and a host function to allow the PoV to reference it. -this issue is that for fast-changing data, you'd need to store all of the different images for 24 hours each. -this would quickly get prohibitive. -Yeah, but then we can not give these tasks that much memory. Maybe around 0.5 MiB to 1 MiB (depending on how well they compress) -yeah -tasks which expect to execute alone could get ~2MB. -Gav -an alternative would be to require the author-network to compute the precise output itself and send it to the storage chain separately. -and if we do this, then up to around 4.5MB. -it's not much by today's desktop standards, but it still beats the shit out of smart contracts. -In reply to -Gav -Gav -and if we do this, then up to around 4.5MB. -Why can we then double it? What do I miss? -5MB PoV limit. -best case you need to provide the full pre-state (with which to initialize the VM) and a hash of the post-state (to verify computation) -however, it's not clear where you get the next block's pre-state from. -one solution is to provide both pre- and post-state into the PoV and piggy-back on Polkadot's 24-hour availability system -(as long as you build the next block at most 24 hours from the last) -You can get the next state by re executing? -Or keep it in your local cache or whatever -but you don't necessarily have other tasks at that time. -or the RC state. -Ahh I see -or, indeed, the pre-state. -PoV disappears after 24 hours. -We can not recalculate all the state -no -this means an average of 5MB / 2 memory bandwidth per block. -minus a bit for smaller tasks to coordinate with gives 2 - 2.5MB. -But if we put the post state into availability system it should work as well? -but we assume the PoV accounts for All of the RCV's resources. -if it doesn't then we should just increase the PoV size. -Yeah fine -i.e. if the RCV can handle 5MB PoV (availability) plus 5MB additional availability, then isn't it just easier to say 10MB PoV? -I think the limit currently is just from the networking side -Aka we could not even really handle these big PoVs -But with async backing it should now be possible -maybe i'm wrong here - if the limit of 5MB PoV is less about availability and more about transport from the collator, then sure, we can split it into a PoV limit and an availability limit -and have 5MB PoV and 10MB availability. -but i assumed that the bottleneck was just availability. -definitely an assumption to test -Yeah we should find out. Maybe I probably neglecting the erasure coding -since there is a difference between data from some other guy (pre-state) and data which you generate yourself (post-state) -assuming i'm right about the bottleneck, then the best we could do without some form of paging (which should be easy enough to implement with jan's help) is having the post-state be computed on the author-network and placed in the storage chain. -jan says that paging is pretty trivial to customise on his RISCV VM. -just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. -Gav -just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. -Yeah, that was also my idea. -But yeah, for the beginning we can probably start with 0.5MiB of memory -we have 16KB pages currently, and we'll already likely be doing something similar for running multiple stacks -Basti.await -But yeah, for the beginning we can probably start with 0.5MiB of memory -yup -definitely enough for a PoC. -but we will still need a new PoV format. -i.e. where we can: -a) request arbitrary data from the RCTaskStorage pallet (which guarantees data placed on it cannot be removed for 24 hours after last read). -b) progress a particular task (in whatever order) with weight spec -c) provide data into a particular task (in whatever order) with weight spec -we might also have a more abstract PoV protocol which allows for different classes of task. -and specifies in Wasm or whatever exactly how to interpret a PoV -then we reformulate the Parachain PoV into this "smart contract". -and we would create a new Task PoV format as just another instance within this overarching protocol. -e.g. this protocol could define UMP, DMP, XCMP and the initial version of the Actors-PoV format might reasonably not include this logic. -but an upgrade later could allow actors to use this stuff. -the main difference with just "hardcoding" it into the RC logic directly is that it could be permissionless - other teams could come up with their own PoV formats and protocols to determine validity. -ok so i've thought about it a bit. -i think there's a route forward for what i referenced up there as "generic PoVs". -in fact they cease to be "PoVs" at this point. -they're just "Work Packages" since you're not proving anything, you're just using the RCVs for compute. -we would define the concept of a Work Class. -all WPs must have a WT. the WT defines what it means to interpret/execute the WP. -we'd initially have two WTs: Parachains and Tasks. -WTs would be permissionless and would come with two main blobs; one (which I'll call map) which can still be in Wasm (since it runs off-chain at a level higher than the PVF) and one (called reduce) which must be strictly metered (so RISCV or very slow Wasm). -as the name suggests, the first represents map and the second represents reduce of a map-reduce pattern. -the first takes the WP as an argument and can inspect any data held in the RCTaskStore. -it then either panics (invalid) or returns a fixed-size (or rather known-maximum-length) blob. -all such blobs and panics are batched up into a Vec and fed into a WT-defined reduce function, with its max_weight = to the block_weight multiplied by the ratio of cores used for this WT compared to all cores. -all WTs have their own child trie. -only this reduce function may alter data in that trie. -for the Parachain WT, this child trie would include all the parachain-specific data (code and state); the ParachainWP output type would basically be the per-chain paras-inherent arguments, and so the PWT reduce function would basically be the paras-inherent logic. -for the Actors WT, this child trie would include Actor specific stuff like codehash (the actual code would be stored in the RCTaskStore) and RISCVVM memory hash, as well as sequence number. -The Actor output would include enough information on which actor-combinations got (maybe) progressed to allow the proper progressions to be recorded in the Actor Child Trie by the reduce function. essentially just the logic i already define in the RFC. -So the Actor map function would interpret the WorkPackage as a manifest and fetch all actor code, initialise each Actor's VM and start them with the entry points according to the manifest. -with this model, anyone could add their own Work Classs. -So if RobK/Arkadiy/Dave can implement the spec, we can focus on writing up the Actor-based map and reduce function. They need not be concerned with Actors at all. -sound sensible? -Good question :P -So the map would have access to fetch the code from the relay chain? -And the map would be trusted code? -That is the same for every WT? -neither map nor reduce are trusted -Gav -neither map nor reduce are trusted -Ahh yeah, you said it -map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. -But the code executed in reduce is defined by the output of map? -yes -Gav -map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. -The code to execute will be referenced by the code hash and this code hash needs to be "somewhere". Currently we store in the relay chain state -yeah, the code would need to be in the "RCTaskStore" (should be the RCWorkStore, i guess) -there is the question about the WorkType Child Trie -This is the child trie you mentioned? -(The RCTaskStore) -RCWorkStore is the on-chain paid data repository which guarantees data remains available for 24 hours after removal -there is also the WorkTypeChildTrie. -i guess this would need to guarantee some sort of dispute-availability also. -Gav -there is also the WorkTypeChildTrie. -This is stored in the RC state? -yes -it's a child trie. -yeah, just wanted to be sure :P -Gav -i guess this would need to guarantee some sort of dispute-availability also. -Yeah for sure. If we need the data as input of the validation -we might need to limit this. -yeah, i think we don't need any of this data. -And map to reduce would be something like Vec }>? -map output would basically be Vec -where struct ProvisionalProgression { actor: ActorId, code: [u8;32], prestate: [u8;32], poststate: [u8;32] } -then reduce would take a Vec> -reduce would check there are no collisions (same actor progressing from same poststate), that the code executed is the expected code hash of the actor and that the prestate fits into a coherent progression of the actor (which might not be the old poststate since the actor could be doing multiple progressions in a single RC block). -So you want to execute the actor code from within reduce? -no -actor code gets executed off-chain in map. -output of map informs reduce what it has done. -it's up to reduce to figure out how to combine all of this into a coherent RC state update. -But when is reduce executed? As part of the RC block execution/building? -yeah -reduce is on-chain, -it accepts a Vec -WorkTypeOutput is an output specific to WorkType, i.e. WorkTypeOutput in fn map(WorkPackage) -> Result -Okay, I see where you want to go. -Gav -then reduce would take a Vec> -Why this, because there can be multiple WorkPackages being progressed in one RC? -yes, one for every core. -those ProvisionalProgressions appearing together in an inner Vec have been co-scheduled. -it might possibly make a difference inside of reduce to know what progressions have happened together on the same core. -Okay. So reduce is getting all the WorkOutputs of all cores for one WT? -precisely. -one WT would be the whole of parachains, UMP, DMP, XCMP. -Yeah -another WT would be the whole actor environment. -Actors and parachains will never be able to talk to each other? -yeah, we can imagine actor-v2 WT which includes the possibility of XCMP/DMP/UMP. -but i would get a basic version of actors done first. -Basti.await -Actors and parachains will never be able to talk to each other? -I more meant "co-scheduled" -just to show it's possible. -In reply to -Basti.await -Basti.await -I more meant "co-scheduled" -perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions -but we should be able to get most of the benefits leaving it as XCMP -if actors work as well as we expect, then the need for chains will dramatically reduce -In reply to -Basti.await -Gav -perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions -Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" -But maybe some simple "was updated in RC block X" should be enough -But yeah, what you propose sounds reasonable -In reply to -Gav -Basti.await -Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" -yeah, i think we would need to provide some sort of hard-coded-function on the RC to migrate between WTs in this case. -parachains couldn't be part of two WTs at once -Okay -but again, i don't see too much benefit in synchronous composability between actors and chains -Yeah -Just wanted to ask -and it complicates things massively -And I think chains are an old concept anyway with coreplay :P -quite. -the level of experimentation this would provide is pretty immense -Yes -This would also quite help for stuff like "elastic scaling" etc. -basically, just turn polkadot into a global, secure map-reduce computer. -We could just experiment -As a new WT -yup -``` \ No newline at end of file From 8e99271c4f9608f728aa6c86d71a4d8dd6edac14 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 27 Sep 2023 13:05:18 +0100 Subject: [PATCH 09/47] Tidy ordering --- text/0031-corejam.md | 769 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 text/0031-corejam.md diff --git a/text/0031-corejam.md b/text/0031-corejam.md new file mode 100644 index 000000000..2802bc007 --- /dev/null +++ b/text/0031-corejam.md @@ -0,0 +1,769 @@ +# RFC-0031: CoreJam + +| | | +| --------------- | ------------------------------------------------------------------------------------------- | +| **Start Date** | 11 September 2023 | +| **Description** | Parallelised, decentralised, permissionless state-machine based on a multistage Collect-Refine-Join-Accumulate model. | +| **Authors** | Gavin Wood, Robert Habermeier, Bastian Köcher | + + +## Summary + +This is a proposal to fundamentally alter the workload done on the Polkadot Relay-chain, both in terms of that which is done "on-chain", i.e. by all Relay Chain Validators (*Validators*) as well as that which is done "in-core", i.e. distributed among subsets of the Validators (*Validator Groups*). The target is to create a model which closely matches the underlying technical architecture and is both generic and permissionlessly extensible. + +In the proposed model, code is stored on-chain with two entry-points. Workloads are collated and processed in-core (and thus parallelized) using one entry-point, whereas the refined outputs of this processing are gathered together and an on-chain state-machine progressed according to the other. + +While somewhat reminiscent of the Map-Reduce paradigm, a comprehensive analogy cannot be taken: the in-core processing code does not transform a set of inputs, but is rather used to refine entirely arbitrary input data collected by some third-party. Instead, and in accordance, we term it *Collect-Refine-Join-Accumulate*. + +## Motivation + +Polkadot was originally designed as a means of validating state transitions of Webassembly-defined state machines known as *Parachain Validation Functions*. These state machines were envisioned to be long-lived (of the order of years) and transitioning continuously, at the "full capacity" of modern single-threaded hardware held in consensus over the internet, and in isolation to any other such state machines. + +Having actually built Polkadot, it became clear that the flexibility of the machinery implementing it allowed for a more diverse set of usage patterns and models. Parathreads, which came to be known as *On-Demand Parachains* (ODP) is one such model. This was underlined by other proposals to allow for a more decentralised administration of how the underlying Polkadot Core resource is procured, in particular *Agile Coretime*. + +More recently, the idea of having small to medium size programs executing without its own surrounding blockchain using only Relay-chain resources has been discussed in detail primarily around the *Coreplay* proposal. It therefore seems short-sighted to assume other models could not exist for utilizing the Relay-chain's "Core" resource. Therefore in much the same way that Agile Coretime originally strived to provide the most general model of *procuring* Relay-chain's Core resource, it seems sensible to strive to find a similarly general model for *utilizing* this resource, one minimizing the difference between the valuable function of the Validators and the service offered by Polkadot. + +Beyond delivering additional value through the increased potential for use-cases that this flexibility allows, our motivation extends to gaining stability: a future-proof platform allowing teams to build on it without fear of high maintenance burden, continuous bitrot or a technological rug-pull at some later date. Secondly, we are motivated by reducing barriers for new teams, allowing the Polkadot platform to harness the power of the crowd which permissionless systems uniquely enable. + +Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the classical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. + +## Requirements + +In order of importance: + +1. The proposal must be compatible, in principle, with the preexisting parachain model. +2. The proposal must facilitate the implementation of Coreplay. +3. The proposal must be compatible with Agile Coretime, as detailed in RFC#0001. +4. Implementation of the proposal should need minimal changes to all production logic. +5. Utilization of Coretime must be accessible. +6. Utilization of Coretime must be permissionless. +7. The nature of Coretime should closely match the nature of resources generated by Polkadot. +8. Minimal opinionation should be introduced over the format, nature and usage of Coretime. + +## Stakeholders + +1. Anyone with exposure to the DOT token economy. +2. Anyone wanting to create decentralised/unstoppable/resilient applications. +3. Teams already building on Polkadot. + +## Explanation + +**CoreJam is a general model for utilization of Polkadot Cores. It is a mechanism by which Work Packages are communicated, authorized, computed and verified, and their results gathered, combined and accumulated into particular parts of the Relay-chain's state.** + +The idea of *Proof-of-Validity* and *Parachain Validation Function* as first-class concepts in the Polkadot protocol is removed. These are now specializations of more general concepts. + +We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Package Output*, *Work Package Result*, *Work Package Report* (also known as a *Candidate*) and *Work Package Attestation*, *Work Class Trie*. + +```rust +mod v0 { + const PROGRESS_WEIGHT_PER_PACKAGE: Weight = MAX_BLOCK_WEIGHT * 3 / 4; + type WorkClass = u32; + type WorkPayload = Vec; + struct WorkItem { + class: WorkClass, + payload: WorkPayload, + } + type MaxWorkItemsInPackage = ConstU32<4>; + type MaxWorkPackagePrerequisites = ConstU32<4>; + enum Authorization { + Instantaneous(InstantaneousAuth), + Bulk(Vec), + } + type HeaderHash = [u8; 32]; + /// Just a Blake2-256 hash of an EncodedWorkPackage. + type WorkPackageHash = [u8; 32]; + struct Context { + header_hash: HeaderHash, + prerequisite: Option, + } + struct WorkPackage { + authorization: Authorization, + context: Context, + items: BoundedVec, + } +} +type MaxWorkPackageSize = ConstU32<5 * 1024 * 1024>; +struct EncodedWorkPackage { + version: u32, + encoded: BoundedVec, +} +impl TryFrom for v0::WorkPackage { + type Error = (); + fn try_from(e: EncodedWorkPackage) -> Result { + match e.version { + 0 => Self::decode(&mut &e.encoded[..]).map_err(|_| ()), + _ => Err(()), + } + } +} +``` + +A *Work Package* is an *Authorization* together with a series of *Work Items* and a context, limited in plurality, versioned and with a maximum encoded size. The Context includes an optional reference to a Work Package (`WorkPackageHash`) which limits the relative order of the Work Package (see Work Package Ordering, later). + +(The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages which a prerequisite which is within another's sequence.) + +Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). + +Though this process happens entirely in consensus, there are two main consensus environments at play, _in-core_ and _on-chain_. We therefore partition the progress into two pairs of stages: Collect & Refine and Join & Accumulate. + +### Processing stages of a Work Package + +A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. + +While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *scope* for the Work Package, allowing its inclusion only on a small portion of the (yet to be built, presumably) Relay-chain. We define the Relay-chain height at this point to be `T`. + +The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. + +The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that the prerequisite it relies upon (if any) has been submitted ahead of it. This is called the initial *Inclusion* on-chain and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. + +At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. + +Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are Accumulated into the common state of the Relay-chain. Here, `a` is any latency incurred due to ordering requirements and is expected to be zero in the variant of this proposal to be implemented initially. See Work Package Ordering, later. + +### Collect-Refine + +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these attestations form the basis for inclusion on-chain and integration into the Relay-chain's state (in the following stages). + +#### Collection and `is_authorized` + +Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. + +There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. + +RCVGs run the risk of a credit owner not having the credit at the point of inclusion, in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RCVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. + +An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RCVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. + +An *Authorizer* is a parameterized procedure: + +```rust +type CodeHash = [u8; 32]; +type AuthParamSize = ConstU32<1024>; +type AuthParam = BoundedVec; +struct Authorizer { + code_hash: CodeHash, + param: AuthParam, +} +``` + +The `code_hash` of the Authorizer is assumed to be the hash of some code accessible in the Relay-chain's Storage pallet. The procedure itself is called the *Authorization Procedure* (`AuthProcedure`) and is expressed in this code (which must be capable of in-core VM execution). Its entry-point prototype is: + +```rust +fn is_authorized(param: &AuthParam, package: &WorkPackage, core_index: CoreIndex) -> bool; +``` + +If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. + +A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. + +Since encoded `Authorizer` values may be fairly large (up to 1,038 bytes here), they may not be a drop-in replacement for the `ParaId`/`TaskId` used at present in the Agile Coretime interface. Because of this, we provide a lookup mechanism allowing a much shorter `AuthId` to be used within the Coretime scheduling messaging. Conveniently, this is precisely the same datatype size (32-bit) as a `ParaId`/`TaskId`. + +There is an Authorizations Pallet which stores the association. Adding a new piece of code is permissionless but requires a deposit commensurate with its size. + +```rust +type AuthId = u32; +type Authorizers = StorageMap; +``` + +An *Authorization* is simply a blob which helps the Authorizer recognize a properly authorized Work Package. No constraints are placed on Authorizers over how they may interpret this blob. Expected authorization content includes signatures, Merkle-proofs and more exotic succinct zero-knowledge proofs. + +_(Note: depending on future Relay-chain Coretime scheduling implementation concerns, a window of Relay-chain blocks)._ + +The need of validators to be rewarded for doing work they might reasonably expect to be useful competes with that of the Coretime procurers to be certain to get work done which is useful to them. In Polkadot 1.0, validators only get rewarded for PoVs ("work packages") which do not panic or overrun. This ensures that validators are well-incentivized to ensure that their computation is useful for the assigned parachain. This incentive model works adequately where all PVF code is of high quality and collators are few and static. + +However with this proposal (and even the advent of on-demand parachains), validators have little ability to identify a high-quality Work Package builder and the permissionless design means a greater expectation of flawed code executing in-core. Because of this, we make a slightly modified approach: Work Packages must have a valid Authorization, i.e. the Coretime-assigned `is_authorized` returns `true` when provided with the Work Package. However, Validators get rewarded for *any* such authorized Work Package, even one which ultimately panics or overruns on its evaluation. + +This ensures that Validators do a strictly limited amount of work before knowing whether they will be rewarded and are able to discontinue and attempt other candidates earlier than would otherwise be the case. There is the possibility of wasting Coretime by processing Work Packages which result in error, but well-written authorization procedures can mitigate this risk by making a prior validation of the Work Items. + +### Refine + +The `refine` function is implemented as an entry-point inside a code blob which is stored on-chain and whose hash is associated with the Work Class. + +```rust +type ClassCodeHash = StorageMap; +``` + +```rust +type WorkOutputLen = ConstU32<1024>; +type WorkOutput = BoundedVec; +fn refine( + payload: WorkPayload, + authorization: Authorization, + auth_id: Option, + context: Context, + package_hash: WorkPackageHash, +) -> WorkOutput; +``` + +Both `refine` and `is_authorized` are only ever executed in-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. + +Several host functions (largely in line with the host functions available to Parachain Validation Function code) are supplied. Two additional ones include: + +```rust +/// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. +fn lookup(hash: [u8; 32]) -> Vec; +/// Determine the state root of the block at given `height`. +fn state_root(height: u32) -> Option<[u8; 32]>; +``` + +Other host functions will allow for the possibility of executing a WebAssembly payload (for example, a Parachain Validation Function) or instantiating and entering a subordinate RISCV VM (for example for Actor Progressions). + +When applying `refine` from the client code, we must allow for the possibility that the VM exits unexpectedly or does not end. Validators are always rewarded for computing properly authorized Work Packages, including those which include such broken Work Items. But they must be able to report their broken state into the Relay-chain in order to collect their reward. Thus we define a type `WorkResult`: + +```rust +enum WorkError { + Timeout, + Panic, +} +struct WorkResult { + class: WorkClass, + item_hash: [u8; 32], + result: Result, + weight: Weight, +} +fn apply_refine(item: WorkItem) -> WorkResult; +``` + +The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. + +```rust +struct WorkReport { + /// The hash of the underlying WorkPackage. + hash: WorkPackageHash, + /// The context of the underlying WorkPackage. + context: Context, + /// The core index of the attesting RcVG. + core_index: CoreIndex, + /// The results of the evaluation of the Items in the underlying Work Package. + results: BoundedVec, +} +/// Multiple signatures are consolidated into a single Attestation in a space +/// efficient manner using a `BitVec` to succinctly which validators have attested. +struct Attestation { + report: WorkReport, + validators: BitVec, // indicates which validators from the group have a signature + attestations: Vec, +} +``` + +Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. + +The process continues once the Attestations arrive at the Relay-chain Block Author. + +### Join-Accumulate + +Join-Accumulate is a second major stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. + +Being on-chain (rather than in-core as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out by the block-author and the resultant block evaluated by all validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. + +The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. + +#### Initial Validation + +There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. + +Firstly, any given Work Report must have enough attestation signatures to be considered for inclusion on-chain. Only one Work Report may be considered for inclusion from each RcVG per block. + +Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. + +```rust +const RECENT_BLOCKS: u32 = 16; +``` + +Thirdly, the RcBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. + +Finally, the RcBA may not include Work Reports whose prerequisite is not itself included in *Recent* blocks. + +In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were included in the *Recent* blocks: + +```rust +const MAX_CORES: u32 = 512; +/// Must be ordered. +type InclusionSet = BoundedVec>; +type RecentInclusions = StorageValue>> +``` + +The RcBA must keep an up to date set of which Work Packages have already been included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. + +While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: + +1. it stops being *Recent*; +2. it becomes included on-chain; or +3. some other Attestation of the same Work Package becomes included on-chain. + +#### Metering + +Join-Accumulate is, as the name suggests, comprised of two subordinate stages. Both stages involve executing code inside a VM on-chain. Thus code must be executed in a *metered* format, meaning it must be able to be executed in a sandboxed and deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. + +Practically speaking, we may allow a similar VM execution metering system similar to that for the `refine` execution, whereby we do not require a strictly deterministic means of interrupting, but do require deterministic metering and only approximate interruption. This would mean that full-nodes and Relay-chain validators could be made to execute some additional margin worth of computation without payment, though any attack could easily be mitigated by attaching a fixed cost (either economically or in weight terms) to an VM invocation. + +Each Work Class defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Report does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. + +```rust +struct WorkItemWeightRequirements { + prune: Weight, + accumulate: Weight, +} +type WeightRequirements = StorageMap; +``` + +Each Work Class has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Work Class logic and represent the amount of weight allotted for each Work Item of this class included in a Work Package assigned to a Core. + +The total amount of weight utilizable by each Work Package (`weight_per_package`) is specified as: + +```rust +weight_per_package = relay_block_weight * safety_margin / max_cores +``` + +`safety_margin` ensures that other Relay-chain system processes can happen and important transactions can be processed and is likely to be around 75%. + +A Work Report is only valid if all weight liabilities of all included Work Items fit within this limit: + +```rust +let total_weight_requirement = work_statement + .items + .map(|item| weight_requirements[item.class]) + .sum(|requirements| requirements.prune + requirements.accumulate); +total_weight_requirement <= weight_per_package +``` + +Because of this, Work Report builders must be aware of any upcoming alterations to `max_cores` and build Statements which are in accordance with it not at present but also in the near future when it may have changed. + +#### Join and `prune` + +_For consideration: Place a hard limit on total weight able to be used by `prune` in any Work Package since it is normally computed twice and an attacker can force it to be computed a third time._ + +_For consideration: Remove `prune` altogether now that we have the Basic Validity Checks._ + +The main difference between code called in the Join stage and that in the Accumulate stage is that in the former code is required to exit successfully, within the weight limit and may not mutate any state. + +The Join stage involves the Relay-chain Block Author gathering together all Work Packages backed by a Validator Group in a manner similar to the current system of PoV candidates. The Work Results are grouped according to their Work Class, and the untrusted Work Class function `prune` is called once for each such group. This returns a `Vec` of invalid indices. Using this result, invalid Work Packages may be pruned and the resultant set retried with confidence that the result will be an empty `Vec`. + +```rust +fn prune(outputs: Vec) -> Vec; +``` + +The call to the `prune` function is allocated weight equal to the length of `outputs` multiplied by the `prune` field of the Work Class's weight requirements. + +The `prune` function is used by the Relay-chain Block Author in order to ensure that all Work Packages which make it through the Join stage are non-conflicting and valid for the present Relay-chain state. All Work Packages are rechecked using the same procedure on-chain and the block is considered malformed (i.e. it panics) if the result is not an empty `Vec`. + +The `prune` function has immutable access to the Work Class's child trie state, as well as regular read-only storage access to the Relay-chain's wider state. + +```rust +fn get_work_storage(key: &[u8]) -> Result>; +fn get_work_storage_len(key: &[u8]); +``` + +#### Accumulate + +The second stage is that of Accumulate. This function is called on-chain only *after* the availability process has completed. + +The function signature to the `accumulate` entry-point in the Work Class's code blob is: + +```rust +fn accumulate(results: Vec<(Authorization, Vec<(ItemHash, WorkResult)>)>); +type ItemHash = [u8; 32]; +``` + +The logic in `accumulate` may need to know how the various Work Items arrived into a processed Work Package. Since a Work Package could have multiple Work Items of the same Work Class, it makes sense to have a separate inner `Vec` for Work Items sharing the Authorization (by virtue of being in the same Work Package). + +Work Items are identified by their Blake2-256 hash, known at the *Item Hash* (`ItemHash`). We provide both the Authorization of the Package and the constituent Work Item Hashes and their Results in order to allow the `refine` logic to take appropriate action in the case that an invalid Work Item was submitted (i.e. one which caused its Refine operation to panic or time-out). + +_(Note for later: We may wish to provide a more light-client friendly Work Item identifier than a simple hash; perhaps a Merkle root of equal-size segments.)_ + +There is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Work Class's weight requirements. + +However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by Classes within one Package may be carried over to the next Package and utilized there. + +```rust +fn get_work_storage(key: &[u8]) -> Result>; +fn get_work_storage_len(key: &[u8]); +fn checkpoint() -> Weight; +fn weight_remaining() -> Weight; +fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; +fn remove_work_storage(key: &[u8]); +``` + +Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `refine` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. + +Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `refine` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. + +Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). + +Other host functions, including some to access Relay-chain hosted services such as the Balances and Storage Pallet may also be provided commensurate with this executing on-chain. + +_(Note for discussion: Should we be considering light-client proof size at all here?)_ + +We can already imagine three kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0), *Actor Progression* (as per Coreplay in a yet-to-be-proposed RFC) and Simple Ordering (placements of elements into a namespaced Merkle trie). Given how abstract the model is, one might reasonably expect many more. + +### Work Package Ordering and Availability + +At the point of Initial Inclusion of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only *not* submit any on-chain which do not have their prerequisite fulfilled. + +However, there is a variable delay between a Work Report first being introduced on-chain in the Initial Inclusion and the Accumulation of it into the Work Class's State due to the Availability Protocol. This means that requiring the order at the point of Initial Inclusion is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. + +Two alternatives present themselves: provide ordering only on a *best-effort* basis, whereby Work Reports respect the ordering requested in their Work Packages as much as possible it is not guaranteed and Work Reports may be Accumulated before, or even entirely without, their prerequisites. We call this *Soft-Ordering*. The alternative is to provide a hard guarantee that the Results of Work Packages will follow the prerequisite requirement. Unable to alter the Availability Protocol this is achieved through on-chain queuing and deferred Accumulation where needed. + +Both are presented as reasonable approaches for this proposal, though the Soft-Ordering variant is expected to be part of any initial implementation. + +#### Soft-Ordering Variant + +In this alternative, actual ordering is only guaranteed going *into* the Availability Protocol, not at the point of Accumulation. + +The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Initial Inclusion is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. + +It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time out and are discarded from Relay-chain state. + +#### Hard-Ordering Variant + +This alternative gives a guarantee that the order in which a Work Package's Items will be Accumulated will respect the stated prerequisite of that Work Package. It is more complex and relies on substantial off-chain logic to inform the on-chain logic about which Work Packages are Accumulatable. + +The (on-chain) repercussion of the Availability Protocol completing for the Work Package depends based on whether it has a prerequisite which is still pending Availability. Since we know that all prerequisite Work Packages must have entered the Availability Protocol due to the Initial Validation, if we are no longer tracking the Work Package and its Work Results on-chain we may safely assume that it is because we have Accumulated them and thus that the Work Package is Available. + +If we are still explicitly retaining a record of the prerequisite Work Package and Work Results, we can assume that the availability process for it has not yet concluded or has already failed. + +If Availability has not yet concluded, we append a reference to (i.e. a hash of) the Work Package to a *Queue of Available Dependents* keyyed by the prerequisite Work Package. The bounding for this list may safely be quite large, and if it grows beyond the bound, the Work Results may be discarded. This could only happen if very many Work Packages are produced and processed with the same prerequisite at approximately the same point in time, and that prerequisite suffers delayed Availability yet the dependents do not: an unlikely eventuality. + +In the case that we are not retaining a record of the prerequisite Work Package and Work Results, we enqueue the Work Results for Accumulation. If there is a non-empty Queue of Available Dependents for this Work Package, we record the fact that this Work Package is *Now Accumulated* in a separate storage item (to circumvent a possibly expensive read/write). If there is not, then we do not record this and will effectively stop tracking the Work Package's Availability status on-chain. + +Finally, after all Availability notices have been processed, but before the Accumulation happens, the RcBA may, via an extrinsic, inform the chain of Work Packages which are the prerequisite preventing available Work Packages from being Accumulated. This amounts to naming a Work Package which has both a Queue of Available Dependents and is Now Accumulated. + +If Availability suffers a time-out (and retrying is not an option), or if the prerequisite has suffered a time-out, then all dependent Work Packages must be discarded in a cascading manner, a potentially daunting proposition. In a manner similar to that above, the time-out itself is recorded in on-chain storage item which itself may only be removed after the latest time at which the newest possible prerequisites (given the Initial Validation) could become available. The RcBAs may then introduce extrinsics to remove these associated storage items in an iterative fashion without the Work Results becoming Accumulated. + +#### Discussion + +An initial implementation of this proposal would almost certainly provide only the soft ordering, since it practically a waypoint to get to the hard ordering implementation. + +Initial Validation are made on-chain prior to the Availabilty Protocol begins for any given Work Package. This ensures that the Work Package is still in scope $-$ i.e. recent enough and on the proper fork. However, this does not ensure that the Work Package is still in scope at the point that the Work Results are actually Accumulated. It is as yet unclear whether this is especially problematic. + +The same scope limit could be placed also on Accumulation; in neither variant does it introduce much additional complexity. In both cases it would require the same course of action as of Availability timing out without the possibility of retry. However whereas in the soft-variant we would not expect to see very differeny dynamics since one such time-out has no repurcissions beyond preventing the Accumulation of those Work Results, in the hard-ordering variant, it could mean a substantially greater occurance of the cascading failure logic calling into question the real purpose of the scoping: is it to protect the Validators from having to deal with indefinitely valid Work Packages or is it to protect the Accumulation logic from having to deal with the Results of older Work Packages? + +### Relay-chain Storage Pallet + +There is a general need to be able to reference large, immutable and long-term data payloads both on-chain and in-core. This is both the case for fixed-function logic such as fetching the VM code for `refine` and `accumulate` as well as from within Work Packages themselves. + +Owing to the potential for forks and disputes to happen beyond the scope of initial validation, there are certain quite subtle requirements over what data held on-chain may be utilized in-core. Because of this, it makes sense to have a general solution which is known to be safe to use in all circumstances. We call this solution the *Storage Pallet*. + +The Storage Pallet provides a simple API, accessible to untrusted code through host functions & extrinsics and to trusted Relay-chain code via a trait interface. + +```rust +trait Storage { + /// Immutable function to attempt to determine the preimage for the given `hash`. + fn lookup(hash: &[u8; 32]) -> Option>; + + /// Allow a particular preimage to be `provide`d. + /// Once provided, this will be available through `lookup` until + /// `unrequest` is called. + fn request(hash: &[u8; 32], len: usize) -> bool; + /// Remove request that some data be made available. If the data was never + /// available or the data will remain available due to another request, + /// then `false` is returned and `expunge` may be called immediately. + /// Otherwise, `true` is returned and `expunge` may be called in + /// 24 hours. + fn unrequest(hash: &[u8; 32]) -> bool; + + // Functions used by implementations of untrusted functions; such as + // extrinsics or host functions. + + /// Place a deposit in order to allow a particular preimage to be `provide`d. + /// Once provided, this will be available through `lookup` until + /// `unrequest_untrusted` is called. + fn request_untrusted(depositor: &AccountId, hash: &[u8; 32], len: usize); + /// Remove request that some data be made available. If the data was never + /// available or the data will remain available due to another request, + /// then `false` is returned and `expunge_untrusted` may be called immediately. + /// Otherwise, `true` is returned and `expunge_untrusted` may be called in + /// 24 hours. + fn unrequest_untrusted(depositor: &AccountId, hash: &[u8; 32]) -> bool; + + // Permissionless items utilizable directly by an extrinsic or task. + + /// Provide the preimage of some requested hash. Returns `Some` if its hash + /// was requested; `None` otherwise. + /// + /// Usually utilized by an extrinsic and is free if `Some` is returned. + fn provide(preimage: &[u8]) -> Option<[u8; 32]>; + /// Potentially remove the preimage of `hash` from the chain when it was + /// unrequested using `unrequest`. `Ok` is returned iff the operation is + /// valid. + /// + /// Usually utilized by a task and is free if it returns `Ok`. + fn expunge(hash: &[u8; 32]) -> Result<(), ()>; + /// Return the deposit associated with the removal of the request by + /// `depositor` using `unrequest_untrusted`. Potentially + /// remove the preimage of `hash` from the chain also. `Ok` is returned + /// iff the operation is valid. + /// + /// Usually utilized by a task and is free if it returns `Ok`. + fn expunge_untrusted(depositor: &AccountId, hash: &[u8; 32]) -> Result<(), ()>; + + /// Equivalent to `request` followed immediately by `provide`. + fn store(data: &[u8]) -> [u8; 32]; +} +``` + +Internally, data is stored with a reference count so that two separate usages of `store` need not be concerned about the other. + +Every piece of data stored for an untrusted caller requires a sizeable deposit. When used by untrusted code via a host function, the `depositor` would be set to an account controlled by the executing code (e.g. the Work Class's sovereign account). + +Removing data happens in a two-phase procedure; first the data is unrequested, signalling that calling `lookup` on its hash may no longer work (it may still work if there are other +requests active). 24 hours following this, the data is expunged with a second call which, actually removes the data from the chain assuming no other requests for it are active. + +Only once expunge is called successfuly is the deposit returned. If the data was never provided, or is additional requests are still active, then expunge may be called immediately after a successful unrequest. + +### Notes on Agile Coretime + +Crucially, a *Task* is no longer a first-class concept. Thus the Agile Coretime model, which in large part allows Coretime to be assigned to a Task Identifier from the Coretime chain, would need to be modified to avoid a hard dependency on this. + +In this proposal, we replace the concept of a Task with a more general ticketing system; Coretime is assigned to an *Authorizer* instead, a parameterized function. This would allow a succinct *Authorization* (i.e. a small blob of data) to be included in the Work Package which, when fed into the relevant Authorizer function could verify that some Work Package is indeed allowed to utilize that Core at (roughly) that time. A simple proof system would be a regular PKI signature. More complex proof systems could include more exotic cryptography (e.g. multisignatures or zk-SNARKs). + +In this model, we would expect any authorized Work Packages which panic or overrun to result in a punishment to the specific author by the logic of the Work Class. + +### Notes for migrating from a Parachain-centric model + +All Parachain-specific data held on the Relay-chain including the means of tracking the Head Data and Code would be held in the Parachains Work Class (Child) Trie. The Work Package would be essentially equivalent to the current PoV blob, though prefixed by the Work Class. `refine` would prove the validity of the parachain transition described in the PoV which is the Work Package. The Parachains Work Output would provide the basis for the input of what is currently termed the Paras Inherent. `accumulate` would identify and resolve any colliding transitions and manage message queue heads, much the same as the current hard-coded logic of the Relay-chain. + +We should consider utilizing the Storage Pallet for Parachain Code and store only a hash in the Parachains Work Class Trie. + +### Notes for implementing the Actor Progression model + +Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Work Class Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Work Class Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. + +### Notes on Implementation Order + +In order to ease the migration process from the current Polkadot on- and off-chain logic to this proposal, we can envision a partial implementation, or refactoring, which would facilitate the eventual proposal whilst remaining compatible with the pre-existing usage and avoid altering substantial code. + +We therefore envision an initial version of this proposal with minimal modifications to current code: + +1. Remain with Webassembly rather than RISC-V, both for Work Class logic and the subordinate environments which can be set up from Work Class logic. The introduction of Work Classes is a permissioned action requiring governance intervention. Work Packages will otherwise execute as per the proposal. *Minor changes to the status quo.* +2. Attested Work Packages must finish running in time and not panic. Therefore `WorkResult` must have an `Infallible` error type. If an Attestation is posted for a Work Package which panics or times out, then this is a slashable offence. *No change to the status quo.* +3. There should be full generalization over Work Package contents, as per the proposal. Introduction of Authorizers, `refine`, `prune` and `accumulate`. *Additional code to the status quo.* + +To optimize the present situation, a number of "natively implemented", fixed-function registrations will be provided. The Work Class of index zero will be used to represent the Parachains Work Class and will have a "native" (i.e. within the Wasm runtime) implementation of `refine` and `accumulate`. Secondly, a fixed-function set of Auth IDs 9,999 and lower simply represent Authorizers which accept Work Packages which contain a single Work Item of the Parachains Work Class which pertain to progressing a parachain of ID equal to the Auth ID value. + +Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Work Classes. + +## Performance, Ergonomics and Compatibility + +This system must be broadly compatible with our existing Parachain Validation Function/Proof-of-Validity model, however a specific feasibility study into transitioning/migration has yet to be completed. + +To aid swift deployment, the Relay-chain may retain its existing parachain-specific logic "hardcoded", and the Coregap logic added separately, with Work Class "zero" being special-cased to mean "the hard-coded Parachain logic". + +## Testing, Security and Privacy + +Standard Polkadot testing and security auditing applies. + +The proposal introduces no new privacy concerns. + +## Future Directions and Related Material + +None at present. + +## Drawbacks, Alternatives and Unknowns + +Important considerations include: + +1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the Work Items. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Work Class State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). + +2. Work Items may need some degree of coordination to be useful by the `accumulate` function of their Work Class. To a large extent this is outside of the scope of this proposal's computation model by design. Through the authorization framework we assert that it is the concern of the Work Class and not of the Relay-chain validators themselves. However we must ensure that certain requirements of the parachains use-case are practically fulfillable in *some* way. Within the legacy parachain model, PoVs: + 1. shouldn't be replayable; + 2. shouldn't require unbounded buffering in `accumulate` if things are submitted out-of-order; + 3. should be possible to evaluate for ordering by validators making a best-effort. + +## Prior Art and References + +None. + +# Chat +``` +for this we need a pallet on the RC to allow arbitrary data to be stored for a deposit, with a safeguard that it would remain in RC state for at least 24 hours (in case of dispute); and a host function to allow the PoV to reference it. +this issue is that for fast-changing data, you'd need to store all of the different images for 24 hours each. +this would quickly get prohibitive. +Yeah, but then we can not give these tasks that much memory. Maybe around 0.5 MiB to 1 MiB (depending on how well they compress) +yeah +tasks which expect to execute alone could get ~2MB. +Gav +an alternative would be to require the author-network to compute the precise output itself and send it to the storage chain separately. +and if we do this, then up to around 4.5MB. +it's not much by today's desktop standards, but it still beats the shit out of smart contracts. +In reply to +Gav +Gav +and if we do this, then up to around 4.5MB. +Why can we then double it? What do I miss? +5MB PoV limit. +best case you need to provide the full pre-state (with which to initialize the VM) and a hash of the post-state (to verify computation) +however, it's not clear where you get the next block's pre-state from. +one solution is to provide both pre- and post-state into the PoV and piggy-back on Polkadot's 24-hour availability system +(as long as you build the next block at most 24 hours from the last) +You can get the next state by re executing? +Or keep it in your local cache or whatever +but you don't necessarily have other tasks at that time. +or the RC state. +Ahh I see +or, indeed, the pre-state. +PoV disappears after 24 hours. +We can not recalculate all the state +no +this means an average of 5MB / 2 memory bandwidth per block. +minus a bit for smaller tasks to coordinate with gives 2 - 2.5MB. +But if we put the post state into availability system it should work as well? +but we assume the PoV accounts for All of the RCV's resources. +if it doesn't then we should just increase the PoV size. +Yeah fine +i.e. if the RCV can handle 5MB PoV (availability) plus 5MB additional availability, then isn't it just easier to say 10MB PoV? +I think the limit currently is just from the networking side +Aka we could not even really handle these big PoVs +But with async backing it should now be possible +maybe i'm wrong here - if the limit of 5MB PoV is less about availability and more about transport from the collator, then sure, we can split it into a PoV limit and an availability limit +and have 5MB PoV and 10MB availability. +but i assumed that the bottleneck was just availability. +definitely an assumption to test +Yeah we should find out. Maybe I probably neglecting the erasure coding +since there is a difference between data from some other guy (pre-state) and data which you generate yourself (post-state) +assuming i'm right about the bottleneck, then the best we could do without some form of paging (which should be easy enough to implement with jan's help) is having the post-state be computed on the author-network and placed in the storage chain. +jan says that paging is pretty trivial to customise on his RISCV VM. +just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. +Gav +just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. +Yeah, that was also my idea. +But yeah, for the beginning we can probably start with 0.5MiB of memory +we have 16KB pages currently, and we'll already likely be doing something similar for running multiple stacks +Basti.await +But yeah, for the beginning we can probably start with 0.5MiB of memory +yup +definitely enough for a PoC. +but we will still need a new PoV format. +i.e. where we can: +a) request arbitrary data from the RCTaskStorage pallet (which guarantees data placed on it cannot be removed for 24 hours after last read). +b) progress a particular task (in whatever order) with weight spec +c) provide data into a particular task (in whatever order) with weight spec +we might also have a more abstract PoV protocol which allows for different classes of task. +and specifies in Wasm or whatever exactly how to interpret a PoV +then we reformulate the Parachain PoV into this "smart contract". +and we would create a new Task PoV format as just another instance within this overarching protocol. +e.g. this protocol could define UMP, DMP, XCMP and the initial version of the Actors-PoV format might reasonably not include this logic. +but an upgrade later could allow actors to use this stuff. +the main difference with just "hardcoding" it into the RC logic directly is that it could be permissionless - other teams could come up with their own PoV formats and protocols to determine validity. +ok so i've thought about it a bit. +i think there's a route forward for what i referenced up there as "generic PoVs". +in fact they cease to be "PoVs" at this point. +they're just "Work Packages" since you're not proving anything, you're just using the RCVs for compute. +we would define the concept of a Work Class. +all WPs must have a WT. the WT defines what it means to interpret/execute the WP. +we'd initially have two WTs: Parachains and Tasks. +WTs would be permissionless and would come with two main blobs; one (which I'll call map) which can still be in Wasm (since it runs off-chain at a level higher than the PVF) and one (called reduce) which must be strictly metered (so RISCV or very slow Wasm). +as the name suggests, the first represents map and the second represents reduce of a map-reduce pattern. +the first takes the WP as an argument and can inspect any data held in the RCTaskStore. +it then either panics (invalid) or returns a fixed-size (or rather known-maximum-length) blob. +all such blobs and panics are batched up into a Vec and fed into a WT-defined reduce function, with its max_weight = to the block_weight multiplied by the ratio of cores used for this WT compared to all cores. +all WTs have their own child trie. +only this reduce function may alter data in that trie. +for the Parachain WT, this child trie would include all the parachain-specific data (code and state); the ParachainWP output type would basically be the per-chain paras-inherent arguments, and so the PWT reduce function would basically be the paras-inherent logic. +for the Actors WT, this child trie would include Actor specific stuff like codehash (the actual code would be stored in the RCTaskStore) and RISCVVM memory hash, as well as sequence number. +The Actor output would include enough information on which actor-combinations got (maybe) progressed to allow the proper progressions to be recorded in the Actor Child Trie by the reduce function. essentially just the logic i already define in the RFC. +So the Actor map function would interpret the WorkPackage as a manifest and fetch all actor code, initialise each Actor's VM and start them with the entry points according to the manifest. +with this model, anyone could add their own Work Classs. +So if RobK/Arkadiy/Dave can implement the spec, we can focus on writing up the Actor-based map and reduce function. They need not be concerned with Actors at all. +sound sensible? +Good question :P +So the map would have access to fetch the code from the relay chain? +And the map would be trusted code? +That is the same for every WT? +neither map nor reduce are trusted +Gav +neither map nor reduce are trusted +Ahh yeah, you said it +map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. +But the code executed in reduce is defined by the output of map? +yes +Gav +map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. +The code to execute will be referenced by the code hash and this code hash needs to be "somewhere". Currently we store in the relay chain state +yeah, the code would need to be in the "RCTaskStore" (should be the RCWorkStore, i guess) +there is the question about the WorkType Child Trie +This is the child trie you mentioned? +(The RCTaskStore) +RCWorkStore is the on-chain paid data repository which guarantees data remains available for 24 hours after removal +there is also the WorkTypeChildTrie. +i guess this would need to guarantee some sort of dispute-availability also. +Gav +there is also the WorkTypeChildTrie. +This is stored in the RC state? +yes +it's a child trie. +yeah, just wanted to be sure :P +Gav +i guess this would need to guarantee some sort of dispute-availability also. +Yeah for sure. If we need the data as input of the validation +we might need to limit this. +yeah, i think we don't need any of this data. +And map to reduce would be something like Vec }>? +map output would basically be Vec +where struct ProvisionalProgression { actor: ActorId, code: [u8;32], prestate: [u8;32], poststate: [u8;32] } +then reduce would take a Vec> +reduce would check there are no collisions (same actor progressing from same poststate), that the code executed is the expected code hash of the actor and that the prestate fits into a coherent progression of the actor (which might not be the old poststate since the actor could be doing multiple progressions in a single RC block). +So you want to execute the actor code from within reduce? +no +actor code gets executed off-chain in map. +output of map informs reduce what it has done. +it's up to reduce to figure out how to combine all of this into a coherent RC state update. +But when is reduce executed? As part of the RC block execution/building? +yeah +reduce is on-chain, +it accepts a Vec +WorkTypeOutput is an output specific to WorkType, i.e. WorkTypeOutput in fn map(WorkPackage) -> Result +Okay, I see where you want to go. +Gav +then reduce would take a Vec> +Why this, because there can be multiple WorkPackages being progressed in one RC? +yes, one for every core. +those ProvisionalProgressions appearing together in an inner Vec have been co-scheduled. +it might possibly make a difference inside of reduce to know what progressions have happened together on the same core. +Okay. So reduce is getting all the WorkOutputs of all cores for one WT? +precisely. +one WT would be the whole of parachains, UMP, DMP, XCMP. +Yeah +another WT would be the whole actor environment. +Actors and parachains will never be able to talk to each other? +yeah, we can imagine actor-v2 WT which includes the possibility of XCMP/DMP/UMP. +but i would get a basic version of actors done first. +Basti.await +Actors and parachains will never be able to talk to each other? +I more meant "co-scheduled" +just to show it's possible. +In reply to +Basti.await +Basti.await +I more meant "co-scheduled" +perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions +but we should be able to get most of the benefits leaving it as XCMP +if actors work as well as we expect, then the need for chains will dramatically reduce +In reply to +Basti.await +Gav +perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions +Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" +But maybe some simple "was updated in RC block X" should be enough +But yeah, what you propose sounds reasonable +In reply to +Gav +Basti.await +Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" +yeah, i think we would need to provide some sort of hard-coded-function on the RC to migrate between WTs in this case. +parachains couldn't be part of two WTs at once +Okay +but again, i don't see too much benefit in synchronous composability between actors and chains +Yeah +Just wanted to ask +and it complicates things massively +And I think chains are an old concept anyway with coreplay :P +quite. +the level of experimentation this would provide is pretty immense +Yes +This would also quite help for stuff like "elastic scaling" etc. +basically, just turn polkadot into a global, secure map-reduce computer. +We could just experiment +As a new WT +yup +``` \ No newline at end of file From 607963e39f0076c1c58d544d8d688d9f5f781bb4 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 27 Sep 2023 13:35:16 +0100 Subject: [PATCH 10/47] More clarity --- text/0031-corejam.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 2802bc007..6464136f3 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -98,7 +98,7 @@ impl TryFrom for v0::WorkPackage { } ``` -A *Work Package* is an *Authorization* together with a series of *Work Items* and a context, limited in plurality, versioned and with a maximum encoded size. The Context includes an optional reference to a Work Package (`WorkPackageHash`) which limits the relative order of the Work Package (see Work Package Ordering, later). +A *Work Package* is an *Authorization* together with a series of *Work Items* and a context, limited in plurality, versioned and with a maximum encoded size. The Context includes an optional reference to a Work Package (`WorkPackageHash`) which limits the relative order of the Work Package (see *Work Package Ordering*, later). (The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages which a prerequisite which is within another's sequence.) @@ -118,7 +118,7 @@ The second consensus computation happens on-chain at the behest of the Relay-cha At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. -Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are Accumulated into the common state of the Relay-chain. Here, `a` is any latency incurred due to ordering requirements and is expected to be zero in the variant of this proposal to be implemented initially. See Work Package Ordering, later. +Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are Accumulated into the common state of the Relay-chain. Here, `a` is any latency incurred due to ordering requirements and is expected to be zero in the variant of this proposal to be implemented initially. See *Work Package Ordering*, later. ### Collect-Refine @@ -262,7 +262,7 @@ The Join-Accumulate stage may be seen as a synchronized counterpart to the paral There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. -Firstly, any given Work Report must have enough attestation signatures to be considered for inclusion on-chain. Only one Work Report may be considered for inclusion from each RcVG per block. +Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Initial Inclusion on-chain. Only one Work Report may be considered for inclusion from each RcVG per block. Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. @@ -272,9 +272,9 @@ const RECENT_BLOCKS: u32 = 16; Thirdly, the RcBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. -Finally, the RcBA may not include Work Reports whose prerequisite is not itself included in *Recent* blocks. +Finally, the RcBA may not include Work Reports whose prerequisite is not itself Initially Included in *Recent* blocks. -In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were included in the *Recent* blocks: +In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were Initially Included in the *Recent* blocks: ```rust const MAX_CORES: u32 = 512; @@ -283,13 +283,19 @@ type InclusionSet = BoundedVec>; type RecentInclusions = StorageValue>> ``` -The RcBA must keep an up to date set of which Work Packages have already been included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. +The RcBA must keep an up to date set of which Work Packages have already been Initially Included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: 1. it stops being *Recent*; -2. it becomes included on-chain; or -3. some other Attestation of the same Work Package becomes included on-chain. +2. it becomes Initially Included on-chain; or +3. some other Attestation of the same Work Package becomes Initially Included on-chain. + +#### Availability + +Once the Work Report of a Work Package is Initially Included on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. + +Only once the a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section *Work Package Ordering*. #### Metering @@ -307,7 +313,7 @@ struct WorkItemWeightRequirements { type WeightRequirements = StorageMap; ``` -Each Work Class has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Work Class logic and represent the amount of weight allotted for each Work Item of this class included in a Work Package assigned to a Core. +Each Work Class has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Work Class logic and represent the amount of weight allotted for each Work Item of this class within in a Work Package assigned to a Core. The total amount of weight utilizable by each Work Package (`weight_per_package`) is specified as: @@ -317,7 +323,7 @@ weight_per_package = relay_block_weight * safety_margin / max_cores `safety_margin` ensures that other Relay-chain system processes can happen and important transactions can be processed and is likely to be around 75%. -A Work Report is only valid if all weight liabilities of all included Work Items fit within this limit: +A Work Report is only valid if all weight liabilities of all Work Items to be Accumulated fit within this limit: ```rust let total_weight_requirement = work_statement @@ -396,7 +402,7 @@ _(Note for discussion: Should we be considering light-client proof size at all h We can already imagine three kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0), *Actor Progression* (as per Coreplay in a yet-to-be-proposed RFC) and Simple Ordering (placements of elements into a namespaced Merkle trie). Given how abstract the model is, one might reasonably expect many more. -### Work Package Ordering and Availability +### Work Package Ordering At the point of Initial Inclusion of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only *not* submit any on-chain which do not have their prerequisite fulfilled. From e71b6ce55640549569deb1f3e53162e864c09a83 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 27 Sep 2023 16:18:10 +0100 Subject: [PATCH 11/47] Cleanups --- text/0031-corejam.md | 133 ++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 45 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 6464136f3..863868593 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -50,9 +50,29 @@ In order of importance: **CoreJam is a general model for utilization of Polkadot Cores. It is a mechanism by which Work Packages are communicated, authorized, computed and verified, and their results gathered, combined and accumulated into particular parts of the Relay-chain's state.** -The idea of *Proof-of-Validity* and *Parachain Validation Function* as first-class concepts in the Polkadot protocol is removed. These are now specializations of more general concepts. +### From Old to New -We introduce a number of new interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Package Output*, *Work Package Result*, *Work Package Report* (also known as a *Candidate*) and *Work Package Attestation*, *Work Class Trie*. +The current specification of the Polkadot protocol and as such Relay-chain operation is designed after a rough proposal and in line with the overall requirements of the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol and to avoid confusion will be known as the *Fixed-function Parachains Legacy Model*, or just *Legacy Model* for short. + +Existing parachains functionality will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. + +As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Work Class Trie*. + +Focussing on continuity and reuse of existing logic, it is unsurprising that many of these have less-general analogues in the Parachains model; while this mapping can be helpful to quickly create an approximate understanding of the new concepts for those already familiar with Polkadot, care must be taken not to inadvertantly make incorrect presumptions over exact details their relationships, constraints, timing, provisions and APIs. Nonetheless, they are provided here for whatever help they may be: + +| CoreJam model | Legacy model | Context | +| --- | --- | --- | +| *Work Package* | Proof-of-Validity | Untrusted data provided to RcVG | +| *Work Item* | Proof-of-Validity | State-transition inputs and witness | +| *Work Output* | Candidate | State-transition consequence | +| *Work Report* | Candidate | Target of Attestation | +| *(Work Package) Attestation* | Attestation | Output signed by members of RcVG | +| *Registration* | Attestation | Placement of Attestation on-chain | +| *Integration* | Inclusion | Irreversible transition of state | +| *Builder* | Collator | | +| *Work Class Trie* | Relay-chain state | Of the Parachains pallets | + +### Overview ```rust mod v0 { @@ -98,7 +118,7 @@ impl TryFrom for v0::WorkPackage { } ``` -A *Work Package* is an *Authorization* together with a series of *Work Items* and a context, limited in plurality, versioned and with a maximum encoded size. The Context includes an optional reference to a Work Package (`WorkPackageHash`) which limits the relative order of the Work Package (see *Work Package Ordering*, later). +A *Work Package* is an *Authorization* together with a series of *Work Items* and a context, limited in plurality, versioned and with a maximum encoded size. The Context includes an optional reference to a Work Package (`WorkPackageHash`) which limits the relative order of the Work Package (see **Work Package Ordering**, later). (The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages which a prerequisite which is within another's sequence.) @@ -110,19 +130,19 @@ Though this process happens entirely in consensus, there are two main consensus A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. -While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *scope* for the Work Package, allowing its inclusion only on a small portion of the (yet to be built, presumably) Relay-chain. We define the Relay-chain height at this point to be `T`. +While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Basic Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. -The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that the prerequisite it relies upon (if any) has been submitted ahead of it. This is called the initial *Inclusion* on-chain and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. +The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that the prerequisite it relies upon (if any) has been submitted ahead of it. This is called the on-chain *Registration* (in the fixed-function parachains model, this is known as "attestation") and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. -At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. +At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. At this point we can say that the Work Report's Package is *Available*. -Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are Accumulated into the common state of the Relay-chain. Here, `a` is any latency incurred due to ordering requirements and is expected to be zero in the variant of this proposal to be implemented initially. See *Work Package Ordering*, later. +Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Work Classes, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `a` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **Work Package Ordering**, later). ### Collect-Refine -The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these attestations form the basis for inclusion on-chain and integration into the Relay-chain's state (in the following stages). +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Work Class's) state which happens in the following stages. #### Collection and `is_authorized` @@ -130,9 +150,9 @@ Collection is the means of a Validator Group member attaining a Work Package whi There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. -RCVGs run the risk of a credit owner not having the credit at the point of inclusion, in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RCVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. +RcVGs run the risk of a credit owner not having the credit at the point of eventual payment (in the Join stage, later), in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RcVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. -An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RCVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. +An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. An *Authorizer* is a parameterized procedure: @@ -227,26 +247,41 @@ fn apply_refine(item: WorkItem) -> WorkResult; The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. ```rust -struct WorkReport { - /// The hash of the underlying WorkPackage. +/// Secure refrence to a Work Package. +struct WorkPackageSpec { + /// The hash of the encoded `EncodedWorkPackage`. hash: WorkPackageHash, - /// The context of the underlying WorkPackage. + /// The erasure root of the encoded `EncodedWorkPackage`. + root: ErasureRoot, + /// The length in bytes of encoded `EncodedWorkPackage`. + len: u32, +} +/// Execution report of a Work Package, mainly comprising the Results from the Refinement +/// of its Work Items. +struct WorkReport { + /// The specification of the underlying Work Package. + package_spec: WorkPackageSpec, + /// The context of the underlying Work Package. context: Context, - /// The core index of the attesting RcVG. + /// The Core index under which the Work Package was Refined to generate the Report. core_index: CoreIndex, /// The results of the evaluation of the Items in the underlying Work Package. results: BoundedVec, } -/// Multiple signatures are consolidated into a single Attestation in a space -/// efficient manner using a `BitVec` to succinctly which validators have attested. +/// Multiple signatures are consolidated into a single Attestation in a space-efficient +/// manner using a `BitVec` to succinctly express which validators have attested. struct Attestation { - report: WorkReport, - validators: BitVec, // indicates which validators from the group have a signature - attestations: Vec, + /// The Work Report which is being attested. + report: WorkReport, + /// Which validators from the group have a signature in `attestations`. + validators: BitVec, + /// The signatures of the RcVG members set out in `validators` whose message is the + /// hash of the `report`. + attestations: Vec, } ``` -Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to include it), then that Validator Group is not rewarded for that block. +Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to introduce it for Registration), then that Validator Group is not rewarded for that block. The process continues once the Attestations arrive at the Relay-chain Block Author. @@ -254,15 +289,23 @@ The process continues once the Attestations arrive at the Relay-chain Block Auth Join-Accumulate is a second major stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. -Being on-chain (rather than in-core as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out by the block-author and the resultant block evaluated by all validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. +Being *on-chain* (rather than *in-core* as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out (initially) by the block-author and the resultant block evaluated by all validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. +#### Registration and Integration + +There are two main phases of on-chain logic before a Work Package's ramifications are irreversibly assimilated into the state of the (current fork of the) Relay-chain. The first is where the Work Package is *Registered* on-chain. This is proposed through an extrinsic introduced by the RcBA and implies the successful outcome of some *Initial Validation* (described next). This kicks-off an off-chain process of *Availability* which, if successful, culminates in a second extrinsic being introduced on-chain shortly afterwards specifying that the Availability requirements of the Work Report are met. + +Since this is an asynchronous process, there are no ordering guarantees on Work Reports' Availability requirements being fulfilled. There may or may not be provision for adding further delays at this point to ensure that Work Reports are processed according to strict ordering. See *Work Package Ordering*, later, for more discussion here. + +Once both Availability and any additional requirements are met (including ordering and dependencies, but possibly also including reevaluation of some of the Initial Validation checks), then the second phase is executed which is known as *Integration*. This is the irreversible application of Work Report consequences into the Work Class's State Trie and (via certain permissionless host functions) the wider state of the Relay-chain. Work Results are segregated into groups based on their Work Class, joined into a `Vec` and passed through the immutable Prune function and into the mutable Accumulate function. + #### Initial Validation -There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. +There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. Sicne the same tests are done on-chain, then for a Block Author to expect to make a valid block these tests must be done prior to actually placing the Attestations in the Relay-chain Block Body. -Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Initial Inclusion on-chain. Only one Work Report may be considered for inclusion from each RcVG per block. +Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Registration on-chain. Only one Work Report may be considered for Registration from each RcVG per block. Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. @@ -270,11 +313,11 @@ Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as h const RECENT_BLOCKS: u32 = 16; ``` -Thirdly, the RcBA may not include multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. +Thirdly, the RcBA may not attempt to Register multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. -Finally, the RcBA may not include Work Reports whose prerequisite is not itself Initially Included in *Recent* blocks. +Finally, the RcBA may not register Work Reports whose prerequisite is not itself Registered in *Recent* blocks. -In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Inclusions* storage item, which retaining all Work Package hashes which were Initially Included in the *Recent* blocks: +In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Registrations* storage item, which retaining all Work Package hashes which were Registered in the *Recent* blocks: ```rust const MAX_CORES: u32 = 512; @@ -283,21 +326,21 @@ type InclusionSet = BoundedVec>; type RecentInclusions = StorageValue>> ``` -The RcBA must keep an up to date set of which Work Packages have already been Initially Included in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. +The RcBA must keep an up to date set of which Work Packages have already been Registered in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. -While it will generally be the case that RCVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RCVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: +While it will generally be the case that RcVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RcVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: 1. it stops being *Recent*; -2. it becomes Initially Included on-chain; or -3. some other Attestation of the same Work Package becomes Initially Included on-chain. +2. it becomes Registered on-chain; or +3. some other Attestation of the same Work Package becomes Registered on-chain. #### Availability -Once the Work Report of a Work Package is Initially Included on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. +Once the Work Report of a Work Package is Registered on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. -Only once the a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section *Work Package Ordering*. +Only once the a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. -#### Metering +#### Weight Provisioning Join-Accumulate is, as the name suggests, comprised of two subordinate stages. Both stages involve executing code inside a VM on-chain. Thus code must be executed in a *metered* format, meaning it must be able to be executed in a sandboxed and deterministic fashion but also with a means of providing an upper limit on the amount of weight it may consume and a guarantee that this limit will never be breached. @@ -404,35 +447,35 @@ We can already imagine three kinds of Work Class: *Parachain Validation* (as per ### Work Package Ordering -At the point of Initial Inclusion of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only *not* submit any on-chain which do not have their prerequisite fulfilled. +At the point of Registration of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only avoid Registering any which do not have their prerequisite fulfilled recently. -However, there is a variable delay between a Work Report first being introduced on-chain in the Initial Inclusion and the Accumulation of it into the Work Class's State due to the Availability Protocol. This means that requiring the order at the point of Initial Inclusion is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. +However, there is a variable delay between a Work Report first being introduced on-chain in the Registration and its eventual Integration into the Work Class's State due to the asynchronous Availability Protocol. This means that requiring the order at the point of Registration is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. -Two alternatives present themselves: provide ordering only on a *best-effort* basis, whereby Work Reports respect the ordering requested in their Work Packages as much as possible it is not guaranteed and Work Reports may be Accumulated before, or even entirely without, their prerequisites. We call this *Soft-Ordering*. The alternative is to provide a hard guarantee that the Results of Work Packages will follow the prerequisite requirement. Unable to alter the Availability Protocol this is achieved through on-chain queuing and deferred Accumulation where needed. +Two alternatives present themselves: provide ordering only on a *best-effort* basis, whereby Work Reports respect the ordering requested in their Work Packages as much as possible, but it is not guaranteed. Work Reports may be Accumulated before, or even entirely without, their prerequisites. We refer to this *Soft-Ordering*. The alternative is to provide a guarantee that the Results of Work Packages will always be Accumulated no earlier than the Result of any prerequisite Work Package. As we are unable to alter the Availability Protocol, this is achieved through on-chain queuing and deferred Accumulation. -Both are presented as reasonable approaches for this proposal, though the Soft-Ordering variant is expected to be part of any initial implementation. +Both are presented as reasonable approaches for this proposal, though the Soft-Ordering variant is expected to be part of any initial implementation since implementation is trivial. #### Soft-Ordering Variant In this alternative, actual ordering is only guaranteed going *into* the Availability Protocol, not at the point of Accumulation. -The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Initial Inclusion is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. +The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Registration is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. -It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time out and are discarded from Relay-chain state. +It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time-out and are discarded from Relay-chain state. #### Hard-Ordering Variant This alternative gives a guarantee that the order in which a Work Package's Items will be Accumulated will respect the stated prerequisite of that Work Package. It is more complex and relies on substantial off-chain logic to inform the on-chain logic about which Work Packages are Accumulatable. -The (on-chain) repercussion of the Availability Protocol completing for the Work Package depends based on whether it has a prerequisite which is still pending Availability. Since we know that all prerequisite Work Packages must have entered the Availability Protocol due to the Initial Validation, if we are no longer tracking the Work Package and its Work Results on-chain we may safely assume that it is because we have Accumulated them and thus that the Work Package is Available. +The (on-chain) repercussion of the Availability Protocol completing for the Work Package depends based on whether it has a prerequisite which is still pending Availability. Since we know that all prerequisite Work Packages must have entered the Availability Protocol due to the Initial Validation, if we are no longer tracking the Work Package and its Work Results on-chain we may safely assume that it is because we have Accumulated them and thus the Work Package is Available. -If we are still explicitly retaining a record of the prerequisite Work Package and Work Results, we can assume that the availability process for it has not yet concluded or has already failed. +Conversely, if the Availability process for the prerequisite Work Package has not yet concluded or has already failed, we ensure that we still explicitly retain a record of it for as long as is needed. -If Availability has not yet concluded, we append a reference to (i.e. a hash of) the Work Package to a *Queue of Available Dependents* keyyed by the prerequisite Work Package. The bounding for this list may safely be quite large, and if it grows beyond the bound, the Work Results may be discarded. This could only happen if very many Work Packages are produced and processed with the same prerequisite at approximately the same point in time, and that prerequisite suffers delayed Availability yet the dependents do not: an unlikely eventuality. +Specifically, if Availability has not yet concluded, we append a hash of the Work Package to a *Queue of Available Dependents* keyyed by the prerequisite Work Package Hash. The bounding for this list may safely be quite large, and if it grows beyond the bound, the Work Results may be discarded. This could only happen if very many Work Packages are produced and processed with the same prerequisite at approximately the same point in time, and that prerequisite suffers delayed Availability yet the dependents do not: an unlikely eventuality. -In the case that we are not retaining a record of the prerequisite Work Package and Work Results, we enqueue the Work Results for Accumulation. If there is a non-empty Queue of Available Dependents for this Work Package, we record the fact that this Work Package is *Now Accumulated* in a separate storage item (to circumvent a possibly expensive read/write). If there is not, then we do not record this and will effectively stop tracking the Work Package's Availability status on-chain. +In the case that we are not retaining a record of the prerequisite Work Package and Work Results, we aggregate the Work Results ready for Accumulation. If there is a non-empty Queue of Available Dependents for this Work Package, we record the fact that this Work Package is *Now Accumulated* in a separate storage item (to circumvent a possibly expensive read/write). If there is not, then we do not record this and will effectively stop tracking the Work Package's Availability status on-chain. -Finally, after all Availability notices have been processed, but before the Accumulation happens, the RcBA may, via an extrinsic, inform the chain of Work Packages which are the prerequisite preventing available Work Packages from being Accumulated. This amounts to naming a Work Package which has both a Queue of Available Dependents and is Now Accumulated. +Finally, after all Availability notices have been processed, but before the Accumulation happens, the RcBA may, via an extrinsic, inform the chain of Work Packages which are prerequisites preventing available Work Packages from being Accumulated. This amounts to naming a Work Package which has both a Queue of Available Dependents and is Now Accumulated. This allows those Work Results to be dequeued and aggregated for Accumulation. If Availability suffers a time-out (and retrying is not an option), or if the prerequisite has suffered a time-out, then all dependent Work Packages must be discarded in a cascading manner, a potentially daunting proposition. In a manner similar to that above, the time-out itself is recorded in on-chain storage item which itself may only be removed after the latest time at which the newest possible prerequisites (given the Initial Validation) could become available. The RcBAs may then introduce extrinsics to remove these associated storage items in an iterative fashion without the Work Results becoming Accumulated. @@ -442,7 +485,7 @@ An initial implementation of this proposal would almost certainly provide only t Initial Validation are made on-chain prior to the Availabilty Protocol begins for any given Work Package. This ensures that the Work Package is still in scope $-$ i.e. recent enough and on the proper fork. However, this does not ensure that the Work Package is still in scope at the point that the Work Results are actually Accumulated. It is as yet unclear whether this is especially problematic. -The same scope limit could be placed also on Accumulation; in neither variant does it introduce much additional complexity. In both cases it would require the same course of action as of Availability timing out without the possibility of retry. However whereas in the soft-variant we would not expect to see very differeny dynamics since one such time-out has no repurcissions beyond preventing the Accumulation of those Work Results, in the hard-ordering variant, it could mean a substantially greater occurance of the cascading failure logic calling into question the real purpose of the scoping: is it to protect the Validators from having to deal with indefinitely valid Work Packages or is it to protect the Accumulation logic from having to deal with the Results of older Work Packages? +The same scope limit could be placed also on Accumulation; in neither variant does it introduce much additional complexity. In both cases it would require the same course of action as of Availability timing out without the possibility of retry. However whereas in the soft-variant we would not expect to see very different dynamics since one such time-out has no repurcissions beyond preventing the Accumulation of those Work Results, in the hard-ordering variant, it could mean a substantially greater occurance of the cascading failure logic calling into question the real purpose of the scoping: is it to protect the Validators from having to deal with indefinitely valid Work Packages or is it to protect the Accumulation logic from having to deal with the Results of older Work Packages? ### Relay-chain Storage Pallet From 3c1d2e558a5e2f562abb78c0b1385edca5e1cdfb Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 27 Sep 2023 16:27:46 +0100 Subject: [PATCH 12/47] More cleanups --- text/0031-corejam.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 863868593..f7ba8d69f 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -52,13 +52,13 @@ In order of importance: ### From Old to New -The current specification of the Polkadot protocol and as such Relay-chain operation is designed after a rough proposal and in line with the overall requirements of the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol and to avoid confusion will be known as the *Fixed-function Parachains Legacy Model*, or just *Legacy Model* for short. +The current specification of the Polkadot protocol, and with in the Relay-chain operation, is designed in line with the overall requirements of and terminology in the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol. To avoid confusion this design will be known as the *Fixed-function Parachains Legacy Model*, or the *Legacy Model* for short. Existing parachains functionality will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Work Class Trie*. -Focussing on continuity and reuse of existing logic, it is unsurprising that many of these have less-general analogues in the Parachains model; while this mapping can be helpful to quickly create an approximate understanding of the new concepts for those already familiar with Polkadot, care must be taken not to inadvertantly make incorrect presumptions over exact details their relationships, constraints, timing, provisions and APIs. Nonetheless, they are provided here for whatever help they may be: +Focussing on continuity and reuse of existing logic, it is unsurprising that many of these concepts already analogues in the Parachains model, albeit ones with a less general definition. While this mapping can be helpful to quickly create an approximate understanding of the new concepts for those already familiar with Polkadot, care must be taken not to inadvertantly make incorrect presumptions over exact details of their relationships, constraints, timing, provisions and APIs. Nonetheless, they are provided here for whatever help they may be. | CoreJam model | Legacy model | Context | | --- | --- | --- | @@ -69,8 +69,9 @@ Focussing on continuity and reuse of existing logic, it is unsurprising that man | *(Work Package) Attestation* | Attestation | Output signed by members of RcVG | | *Registration* | Attestation | Placement of Attestation on-chain | | *Integration* | Inclusion | Irreversible transition of state | -| *Builder* | Collator | | -| *Work Class Trie* | Relay-chain state | Of the Parachains pallets | +| *Builder* | Collator | Creator of data worthy of Attestation | + +Additionally, the *Work Class Trie* has no immediate analogue, but may be considered as the Relay-chain state used to track the code and head data of the parachains. ### Overview From 329e24f511bf6f8eab8f88a71efe453037d0568c Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 27 Sep 2023 17:53:08 +0100 Subject: [PATCH 13/47] Compatibility --- text/0031-corejam.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index f7ba8d69f..c67bd3b1d 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -52,9 +52,9 @@ In order of importance: ### From Old to New -The current specification of the Polkadot protocol, and with in the Relay-chain operation, is designed in line with the overall requirements of and terminology in the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol. To avoid confusion this design will be known as the *Fixed-function Parachains Legacy Model*, or the *Legacy Model* for short. +The current specification of the Polkadot protocol, and with in the Relay-chain operation, is designed in line with the overall requirements of and terminology in the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol. To avoid confusion, this design will be known as the *Fixed-function Parachains Legacy Model*, or the *Legacy Model* for short. -Existing parachains functionality will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. +Existing functionality relied upon by parachains will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. Transition of Polkadot to be in line with the present proposal will be necessarily imply some minor alterations of formats utilized by Cumulus, Smoldot and other light-client APIs (see the section on Compatibility). However, much of the underlying logic (in particular, consensus, disputes and availability) is retained, though its application is generalised. This proposal will only make note of the expectations regarding the changes, and presumes continuation of all other logic. As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Work Class Trie*. @@ -593,11 +593,15 @@ To optimize the present situation, a number of "natively implemented", fixed-fun Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Work Classes. +A final transition may migrate the Parachains Work Class to become a regular permissionless Work Class module. + ## Performance, Ergonomics and Compatibility -This system must be broadly compatible with our existing Parachain Validation Function/Proof-of-Validity model, however a specific feasibility study into transitioning/migration has yet to be completed. +The present proposal is broadly compatible with the facilities of the Legacy Model pending the integration of a Work Class specific to Parachains. Unlike other Work Classes, this is expected to be hard-coded into the Relay-chain runtime to maximize performance and minimize implementation time. + +Certain changes to active interfaces will be needed. Firstly, changes will be needed for any software (such as _Cumulus_ and _Smoldot_) relying on particular Relay-chain state trie keys (i.e. storage locations) used to track the code and head-data of parachains, so that they instead query the relevant key within the Parachains Work Class Child Trie. -To aid swift deployment, the Relay-chain may retain its existing parachain-specific logic "hardcoded", and the Coregap logic added separately, with Work Class "zero" being special-cased to mean "the hard-coded Parachain logic". +Secondly, software which currently provides Proofs-of-Validity to Relay-chain Validators, such as _Cumulus_, would need to be updated to use the new Work Item/Work Package format. ## Testing, Security and Privacy From dae162c8d4d2bf76796c78b74529bea86c37d013 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 27 Sep 2023 17:55:14 +0100 Subject: [PATCH 14/47] More cleanups --- text/0031-corejam.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index c67bd3b1d..46dd186c2 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -628,7 +628,10 @@ Important considerations include: None. -# Chat +# Historical + +#### Chat Gav/Basti, 2023-09-11 + ``` for this we need a pallet on the RC to allow arbitrary data to be stored for a deposit, with a safeguard that it would remain in RC state for at least 24 hours (in case of dispute); and a host function to allow the PoV to reference it. this issue is that for fast-changing data, you'd need to store all of the different images for 24 hours each. From b2a05bae26e18c33cfcbdb2798fc6044dee5420a Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 28 Sep 2023 15:51:26 +0100 Subject: [PATCH 15/47] Register becomes Report --- text/0031-corejam.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 46dd186c2..489473173 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -67,7 +67,7 @@ Focussing on continuity and reuse of existing logic, it is unsurprising that man | *Work Output* | Candidate | State-transition consequence | | *Work Report* | Candidate | Target of Attestation | | *(Work Package) Attestation* | Attestation | Output signed by members of RcVG | -| *Registration* | Attestation | Placement of Attestation on-chain | +| *Reporting* | Attestation | Placement of Attestation on-chain | | *Integration* | Inclusion | Irreversible transition of state | | *Builder* | Collator | Creator of data worthy of Attestation | @@ -135,7 +135,7 @@ While a Work Package is being built, the *Builder* must have a synchronized Rela The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. -The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that the prerequisite it relies upon (if any) has been submitted ahead of it. This is called the on-chain *Registration* (in the fixed-function parachains model, this is known as "attestation") and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. +The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that the prerequisite it relies upon (if any) has been submitted ahead of it. This is called the on-chain *Reporting* (in the fixed-function parachains model, this is known as "attestation") and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. At this point we can say that the Work Report's Package is *Available*. @@ -282,7 +282,7 @@ struct Attestation { } ``` -Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to introduce it for Registration), then that Validator Group is not rewarded for that block. +Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to introduce it for Reporting), then that Validator Group is not rewarded for that block. The process continues once the Attestations arrive at the Relay-chain Block Author. @@ -294,7 +294,7 @@ Being *on-chain* (rather than *in-core* as with Collect-Refine), information and The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. -#### Registration and Integration +#### Reporting and Integration There are two main phases of on-chain logic before a Work Package's ramifications are irreversibly assimilated into the state of the (current fork of the) Relay-chain. The first is where the Work Package is *Registered* on-chain. This is proposed through an extrinsic introduced by the RcBA and implies the successful outcome of some *Initial Validation* (described next). This kicks-off an off-chain process of *Availability* which, if successful, culminates in a second extrinsic being introduced on-chain shortly afterwards specifying that the Availability requirements of the Work Report are met. @@ -306,7 +306,7 @@ Once both Availability and any additional requirements are met (including orderi There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. Sicne the same tests are done on-chain, then for a Block Author to expect to make a valid block these tests must be done prior to actually placing the Attestations in the Relay-chain Block Body. -Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Registration on-chain. Only one Work Report may be considered for Registration from each RcVG per block. +Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Reporting on-chain. Only one Work Report may be considered for Reporting from each RcVG per block. Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. @@ -314,7 +314,7 @@ Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as h const RECENT_BLOCKS: u32 = 16; ``` -Thirdly, the RcBA may not attempt to Register multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. +Thirdly, the RcBA may not attempt to Report multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. Finally, the RcBA may not register Work Reports whose prerequisite is not itself Registered in *Recent* blocks. @@ -448,9 +448,9 @@ We can already imagine three kinds of Work Class: *Parachain Validation* (as per ### Work Package Ordering -At the point of Registration of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only avoid Registering any which do not have their prerequisite fulfilled recently. +At the point of Reporting of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only avoid Registering any which do not have their prerequisite fulfilled recently. -However, there is a variable delay between a Work Report first being introduced on-chain in the Registration and its eventual Integration into the Work Class's State due to the asynchronous Availability Protocol. This means that requiring the order at the point of Registration is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. +However, there is a variable delay between a Work Report first being introduced on-chain in the Reporting and its eventual Integration into the Work Class's State due to the asynchronous Availability Protocol. This means that requiring the order at the point of Reporting is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. Two alternatives present themselves: provide ordering only on a *best-effort* basis, whereby Work Reports respect the ordering requested in their Work Packages as much as possible, but it is not guaranteed. Work Reports may be Accumulated before, or even entirely without, their prerequisites. We refer to this *Soft-Ordering*. The alternative is to provide a guarantee that the Results of Work Packages will always be Accumulated no earlier than the Result of any prerequisite Work Package. As we are unable to alter the Availability Protocol, this is achieved through on-chain queuing and deferred Accumulation. @@ -460,7 +460,7 @@ Both are presented as reasonable approaches for this proposal, though the Soft-O In this alternative, actual ordering is only guaranteed going *into* the Availability Protocol, not at the point of Accumulation. -The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Registration is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. +The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Reporting is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time-out and are discarded from Relay-chain state. From 6d5a175572c74847054ae47e724a6caa5f877b04 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 30 Sep 2023 14:37:07 +0100 Subject: [PATCH 16/47] UMP/HRMP compat --- text/0031-corejam.md | 215 +++---------------------------------------- 1 file changed, 14 insertions(+), 201 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 489473173..38e77700d 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -205,7 +205,7 @@ type ClassCodeHash = StorageMap; ``` ```rust -type WorkOutputLen = ConstU32<1024>; +type WorkOutputLen = ConstU32<4_096>; type WorkOutput = BoundedVec; fn refine( payload: WorkPayload, @@ -597,12 +597,22 @@ A final transition may migrate the Parachains Work Class to become a regular per ## Performance, Ergonomics and Compatibility -The present proposal is broadly compatible with the facilities of the Legacy Model pending the integration of a Work Class specific to Parachains. Unlike other Work Classes, this is expected to be hard-coded into the Relay-chain runtime to maximize performance and minimize implementation time. +The present proposal is broadly compatible with the facilities of the Legacy Model pending the integration of a Work Class specific to Parachains. Unlike other Work Classes, this is expected to be hard-coded into the Relay-chain runtime to maximize performance, compatibility and implementation speed. -Certain changes to active interfaces will be needed. Firstly, changes will be needed for any software (such as _Cumulus_ and _Smoldot_) relying on particular Relay-chain state trie keys (i.e. storage locations) used to track the code and head-data of parachains, so that they instead query the relevant key within the Parachains Work Class Child Trie. +Certain changes to active interfaces will be needed. Firstly, changes will be needed for any software (such as _Cumulus_ and _Smoldot_) relying on particular Relay-chain state trie keys (i.e. storage locations) used to track the code and head-data of parachains, so that they instead query the relevant key within the Parachains Work Class Child Trie. Secondly, software which currently provides Proofs-of-Validity to Relay-chain Validators, such as _Cumulus_, would need to be updated to use the new Work Item/Work Package format. +### UMP and HRMP + +At present, both HRMP (the stop-gap measure introduced in lieu of proper XCMP) and UMP, make substantial usage of the ability for parachains to include data in their PoV which will be interpreted on-chain by the Relay-chain. The current limit of UMP alone is 1MB. Even with the current amount of parachains, it is not possible for all parachains to be able to make use of these resources within the same block, and the difficult problem of apportioning resources in the case of contest is structurally unsolved and left for the RcBA to make an arbitrary selection. + +The present proposal aims to bring some clarity to this situation by limiting the amount of data which can arrive on the Relay-chain from each Work Item, and by extension from each Work Package. The specific limit proposed is 4KB per Work Item, which if we assume an average of two Work Items per Package and 250 cores, comes to a manageable 2MB and leaves plenty of headroom. + +However, this does mean that pre-existing usage of UMP and HRMP would be substanially impaired. For example, under these limits a future Staking System-chain would be unable to send 1,000 sets of validator keys (each taking up to around 100 bytes) to the Relay-chain within a single block. + +The overall situation will be improved substantially through preexisting plans, particularly the development and deployment of XCMP to avoid the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. The "Hermit-Relay" concept of spinning out ancilliary functionality handled by the Relay-chain to System chains will also alleviate the situation somewhat. Taken together, HRMP will no longer exist and UMP will be limited to a few System-chains making well-bounded interactions. + ## Testing, Security and Privacy Standard Polkadot testing and security auditing applies. @@ -611,7 +621,7 @@ The proposal introduces no new privacy concerns. ## Future Directions and Related Material -None at present. +We expect to see several Work Classes being built shortly after CorePlay is delivered. ## Drawbacks, Alternatives and Unknowns @@ -627,200 +637,3 @@ Important considerations include: ## Prior Art and References None. - -# Historical - -#### Chat Gav/Basti, 2023-09-11 - -``` -for this we need a pallet on the RC to allow arbitrary data to be stored for a deposit, with a safeguard that it would remain in RC state for at least 24 hours (in case of dispute); and a host function to allow the PoV to reference it. -this issue is that for fast-changing data, you'd need to store all of the different images for 24 hours each. -this would quickly get prohibitive. -Yeah, but then we can not give these tasks that much memory. Maybe around 0.5 MiB to 1 MiB (depending on how well they compress) -yeah -tasks which expect to execute alone could get ~2MB. -Gav -an alternative would be to require the author-network to compute the precise output itself and send it to the storage chain separately. -and if we do this, then up to around 4.5MB. -it's not much by today's desktop standards, but it still beats the shit out of smart contracts. -In reply to -Gav -Gav -and if we do this, then up to around 4.5MB. -Why can we then double it? What do I miss? -5MB PoV limit. -best case you need to provide the full pre-state (with which to initialize the VM) and a hash of the post-state (to verify computation) -however, it's not clear where you get the next block's pre-state from. -one solution is to provide both pre- and post-state into the PoV and piggy-back on Polkadot's 24-hour availability system -(as long as you build the next block at most 24 hours from the last) -You can get the next state by re executing? -Or keep it in your local cache or whatever -but you don't necessarily have other tasks at that time. -or the RC state. -Ahh I see -or, indeed, the pre-state. -PoV disappears after 24 hours. -We can not recalculate all the state -no -this means an average of 5MB / 2 memory bandwidth per block. -minus a bit for smaller tasks to coordinate with gives 2 - 2.5MB. -But if we put the post state into availability system it should work as well? -but we assume the PoV accounts for All of the RCV's resources. -if it doesn't then we should just increase the PoV size. -Yeah fine -i.e. if the RCV can handle 5MB PoV (availability) plus 5MB additional availability, then isn't it just easier to say 10MB PoV? -I think the limit currently is just from the networking side -Aka we could not even really handle these big PoVs -But with async backing it should now be possible -maybe i'm wrong here - if the limit of 5MB PoV is less about availability and more about transport from the collator, then sure, we can split it into a PoV limit and an availability limit -and have 5MB PoV and 10MB availability. -but i assumed that the bottleneck was just availability. -definitely an assumption to test -Yeah we should find out. Maybe I probably neglecting the erasure coding -since there is a difference between data from some other guy (pre-state) and data which you generate yourself (post-state) -assuming i'm right about the bottleneck, then the best we could do without some form of paging (which should be easy enough to implement with jan's help) is having the post-state be computed on the author-network and placed in the storage chain. -jan says that paging is pretty trivial to customise on his RISCV VM. -just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. -Gav -just a segfault everytime a new page is required and we can either suspend + snapshot; or fetch + assign the page and continue. -Yeah, that was also my idea. -But yeah, for the beginning we can probably start with 0.5MiB of memory -we have 16KB pages currently, and we'll already likely be doing something similar for running multiple stacks -Basti.await -But yeah, for the beginning we can probably start with 0.5MiB of memory -yup -definitely enough for a PoC. -but we will still need a new PoV format. -i.e. where we can: -a) request arbitrary data from the RCTaskStorage pallet (which guarantees data placed on it cannot be removed for 24 hours after last read). -b) progress a particular task (in whatever order) with weight spec -c) provide data into a particular task (in whatever order) with weight spec -we might also have a more abstract PoV protocol which allows for different classes of task. -and specifies in Wasm or whatever exactly how to interpret a PoV -then we reformulate the Parachain PoV into this "smart contract". -and we would create a new Task PoV format as just another instance within this overarching protocol. -e.g. this protocol could define UMP, DMP, XCMP and the initial version of the Actors-PoV format might reasonably not include this logic. -but an upgrade later could allow actors to use this stuff. -the main difference with just "hardcoding" it into the RC logic directly is that it could be permissionless - other teams could come up with their own PoV formats and protocols to determine validity. -ok so i've thought about it a bit. -i think there's a route forward for what i referenced up there as "generic PoVs". -in fact they cease to be "PoVs" at this point. -they're just "Work Packages" since you're not proving anything, you're just using the RCVs for compute. -we would define the concept of a Work Class. -all WPs must have a WT. the WT defines what it means to interpret/execute the WP. -we'd initially have two WTs: Parachains and Tasks. -WTs would be permissionless and would come with two main blobs; one (which I'll call map) which can still be in Wasm (since it runs off-chain at a level higher than the PVF) and one (called reduce) which must be strictly metered (so RISCV or very slow Wasm). -as the name suggests, the first represents map and the second represents reduce of a map-reduce pattern. -the first takes the WP as an argument and can inspect any data held in the RCTaskStore. -it then either panics (invalid) or returns a fixed-size (or rather known-maximum-length) blob. -all such blobs and panics are batched up into a Vec and fed into a WT-defined reduce function, with its max_weight = to the block_weight multiplied by the ratio of cores used for this WT compared to all cores. -all WTs have their own child trie. -only this reduce function may alter data in that trie. -for the Parachain WT, this child trie would include all the parachain-specific data (code and state); the ParachainWP output type would basically be the per-chain paras-inherent arguments, and so the PWT reduce function would basically be the paras-inherent logic. -for the Actors WT, this child trie would include Actor specific stuff like codehash (the actual code would be stored in the RCTaskStore) and RISCVVM memory hash, as well as sequence number. -The Actor output would include enough information on which actor-combinations got (maybe) progressed to allow the proper progressions to be recorded in the Actor Child Trie by the reduce function. essentially just the logic i already define in the RFC. -So the Actor map function would interpret the WorkPackage as a manifest and fetch all actor code, initialise each Actor's VM and start them with the entry points according to the manifest. -with this model, anyone could add their own Work Classs. -So if RobK/Arkadiy/Dave can implement the spec, we can focus on writing up the Actor-based map and reduce function. They need not be concerned with Actors at all. -sound sensible? -Good question :P -So the map would have access to fetch the code from the relay chain? -And the map would be trusted code? -That is the same for every WT? -neither map nor reduce are trusted -Gav -neither map nor reduce are trusted -Ahh yeah, you said it -map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. -But the code executed in reduce is defined by the output of map? -yes -Gav -map would only be able to read the part of RC state which is guaranteed to be available for 24 hours; this basically just means the RCTaskStorage. -The code to execute will be referenced by the code hash and this code hash needs to be "somewhere". Currently we store in the relay chain state -yeah, the code would need to be in the "RCTaskStore" (should be the RCWorkStore, i guess) -there is the question about the WorkType Child Trie -This is the child trie you mentioned? -(The RCTaskStore) -RCWorkStore is the on-chain paid data repository which guarantees data remains available for 24 hours after removal -there is also the WorkTypeChildTrie. -i guess this would need to guarantee some sort of dispute-availability also. -Gav -there is also the WorkTypeChildTrie. -This is stored in the RC state? -yes -it's a child trie. -yeah, just wanted to be sure :P -Gav -i guess this would need to guarantee some sort of dispute-availability also. -Yeah for sure. If we need the data as input of the validation -we might need to limit this. -yeah, i think we don't need any of this data. -And map to reduce would be something like Vec }>? -map output would basically be Vec -where struct ProvisionalProgression { actor: ActorId, code: [u8;32], prestate: [u8;32], poststate: [u8;32] } -then reduce would take a Vec> -reduce would check there are no collisions (same actor progressing from same poststate), that the code executed is the expected code hash of the actor and that the prestate fits into a coherent progression of the actor (which might not be the old poststate since the actor could be doing multiple progressions in a single RC block). -So you want to execute the actor code from within reduce? -no -actor code gets executed off-chain in map. -output of map informs reduce what it has done. -it's up to reduce to figure out how to combine all of this into a coherent RC state update. -But when is reduce executed? As part of the RC block execution/building? -yeah -reduce is on-chain, -it accepts a Vec -WorkTypeOutput is an output specific to WorkType, i.e. WorkTypeOutput in fn map(WorkPackage) -> Result -Okay, I see where you want to go. -Gav -then reduce would take a Vec> -Why this, because there can be multiple WorkPackages being progressed in one RC? -yes, one for every core. -those ProvisionalProgressions appearing together in an inner Vec have been co-scheduled. -it might possibly make a difference inside of reduce to know what progressions have happened together on the same core. -Okay. So reduce is getting all the WorkOutputs of all cores for one WT? -precisely. -one WT would be the whole of parachains, UMP, DMP, XCMP. -Yeah -another WT would be the whole actor environment. -Actors and parachains will never be able to talk to each other? -yeah, we can imagine actor-v2 WT which includes the possibility of XCMP/DMP/UMP. -but i would get a basic version of actors done first. -Basti.await -Actors and parachains will never be able to talk to each other? -I more meant "co-scheduled" -just to show it's possible. -In reply to -Basti.await -Basti.await -I more meant "co-scheduled" -perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions -but we should be able to get most of the benefits leaving it as XCMP -if actors work as well as we expect, then the need for chains will dramatically reduce -In reply to -Basti.await -Gav -perhaps for actors-v3 which would allow WPs to include both parachain and actor progressions -Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" -But maybe some simple "was updated in RC block X" should be enough -But yeah, what you propose sounds reasonable -In reply to -Gav -Basti.await -Okay, my only comment on this would be that we then need to ensure that parachains and actors are not scheduled in different groups of "WTs" -yeah, i think we would need to provide some sort of hard-coded-function on the RC to migrate between WTs in this case. -parachains couldn't be part of two WTs at once -Okay -but again, i don't see too much benefit in synchronous composability between actors and chains -Yeah -Just wanted to ask -and it complicates things massively -And I think chains are an old concept anyway with coreplay :P -quite. -the level of experimentation this would provide is pretty immense -Yes -This would also quite help for stuff like "elastic scaling" etc. -basically, just turn polkadot into a global, secure map-reduce computer. -We could just experiment -As a new WT -yup -``` \ No newline at end of file From 5fd33ac84a8727cf4451ea26b6a5c50ccd2ed0b4 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 30 Sep 2023 16:19:25 +0100 Subject: [PATCH 17/47] More on copmat --- text/0031-corejam.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 38e77700d..ec683fccd 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -613,6 +613,8 @@ However, this does mean that pre-existing usage of UMP and HRMP would be substan The overall situation will be improved substantially through preexisting plans, particularly the development and deployment of XCMP to avoid the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. The "Hermit-Relay" concept of spinning out ancilliary functionality handled by the Relay-chain to System chains will also alleviate the situation somewhat. Taken together, HRMP will no longer exist and UMP will be limited to a few System-chains making well-bounded interactions. +An initial deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Work Class to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. + ## Testing, Security and Privacy Standard Polkadot testing and security auditing applies. From eb7dab2ff9fc02d38234d7b70b65804506597fef Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 30 Sep 2023 16:39:03 +0100 Subject: [PATCH 18/47] Move para --- text/0031-corejam.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index ec683fccd..828e17401 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -579,6 +579,18 @@ We should consider utilizing the Storage Pallet for Parachain Code and store onl Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Work Class Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Work Class Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. +### UMP, HRMP and Work Output bounding + +At present, both HRMP (the stop-gap measure introduced in lieu of proper XCMP) and UMP, make substantial usage of the ability for parachains to include data in their PoV which will be interpreted on-chain by the Relay-chain. The current limit of UMP alone is 1MB. Even with the current amount of parachains, it is not possible for all parachains to be able to make use of these resources within the same block, and the difficult problem of apportioning resources in the case of contest is structurally unsolved and left for the RcBA to make an arbitrary selection. + +The present proposal aims to bring some clarity to this situation by limiting the amount of data which can arrive on the Relay-chain from each Work Item, and by extension from each Work Package. The specific limit proposed is 4KB per Work Item, which if we assume an average of two Work Items per Package and 250 cores, comes to a manageable 2MB and leaves plenty of headroom. + +However, this does mean that pre-existing usage of UMP and HRMP would be substanially impaired. For example, under these limits a future Staking System-chain would be unable to send 1,000 sets of validator keys (each taking up to around 100 bytes) to the Relay-chain within a single block. + +The overall situation will be improved substantially through preexisting plans, particularly the development and deployment of XCMP to avoid the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. The "Hermit-Relay" concept of spinning out ancilliary functionality handled by the Relay-chain to System chains will also alleviate the situation somewhat. Taken together, HRMP will no longer exist and UMP will be limited to a few System-chains making well-bounded interactions. + +An initial deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Work Class to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. + ### Notes on Implementation Order In order to ease the migration process from the current Polkadot on- and off-chain logic to this proposal, we can envision a partial implementation, or refactoring, which would facilitate the eventual proposal whilst remaining compatible with the pre-existing usage and avoid altering substantial code. @@ -603,18 +615,6 @@ Certain changes to active interfaces will be needed. Firstly, changes will be ne Secondly, software which currently provides Proofs-of-Validity to Relay-chain Validators, such as _Cumulus_, would need to be updated to use the new Work Item/Work Package format. -### UMP and HRMP - -At present, both HRMP (the stop-gap measure introduced in lieu of proper XCMP) and UMP, make substantial usage of the ability for parachains to include data in their PoV which will be interpreted on-chain by the Relay-chain. The current limit of UMP alone is 1MB. Even with the current amount of parachains, it is not possible for all parachains to be able to make use of these resources within the same block, and the difficult problem of apportioning resources in the case of contest is structurally unsolved and left for the RcBA to make an arbitrary selection. - -The present proposal aims to bring some clarity to this situation by limiting the amount of data which can arrive on the Relay-chain from each Work Item, and by extension from each Work Package. The specific limit proposed is 4KB per Work Item, which if we assume an average of two Work Items per Package and 250 cores, comes to a manageable 2MB and leaves plenty of headroom. - -However, this does mean that pre-existing usage of UMP and HRMP would be substanially impaired. For example, under these limits a future Staking System-chain would be unable to send 1,000 sets of validator keys (each taking up to around 100 bytes) to the Relay-chain within a single block. - -The overall situation will be improved substantially through preexisting plans, particularly the development and deployment of XCMP to avoid the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. The "Hermit-Relay" concept of spinning out ancilliary functionality handled by the Relay-chain to System chains will also alleviate the situation somewhat. Taken together, HRMP will no longer exist and UMP will be limited to a few System-chains making well-bounded interactions. - -An initial deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Work Class to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. - ## Testing, Security and Privacy Standard Polkadot testing and security auditing applies. From 0b5b7ab2a206821c9b463f446d3557733f9045aa Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 30 Sep 2023 20:11:52 +0100 Subject: [PATCH 19/47] Typo --- text/0031-corejam.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 828e17401..3ee9e6779 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -426,17 +426,20 @@ There is an amount of weight which it is allowed to use before being forcibly te However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by Classes within one Package may be carried over to the next Package and utilized there. ```rust -fn get_work_storage(key: &[u8]) -> Result>; -fn get_work_storage_len(key: &[u8]); +fn get_work_storage(key: &[u8]) -> Option>; +fn get_work_storage_len(key: &[u8]) -> Option; fn checkpoint() -> Weight; fn weight_remaining() -> Weight; fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; fn remove_work_storage(key: &[u8]); +fn ump_enqueue(message: &[u8]) -> Result<(), ()>; ``` -Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `refine` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. +Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. + +UMP messages for the Relay-chain to interpret may be enqueued through `ump_enqueue` function. For this to succeed, the Relay-chain must have pre-authorized the use of UMP for this endpoint. -Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `refine` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. +Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `accumulate` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). From 26fd0ed33e6fdbc9ee416302dc7a624f717167b4 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 2 Oct 2023 16:56:42 +0100 Subject: [PATCH 20/47] Update text/0031-corejam.md Co-authored-by: asynchronous rob --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 3ee9e6779..7c10f59b4 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -304,7 +304,7 @@ Once both Availability and any additional requirements are met (including orderi #### Initial Validation -There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. Sicne the same tests are done on-chain, then for a Block Author to expect to make a valid block these tests must be done prior to actually placing the Attestations in the Relay-chain Block Body. +There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. Since the same tests are done on-chain, then for a Block Author to expect to make a valid block these tests must be done prior to actually placing the Attestations in the Relay-chain Block Body. Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Reporting on-chain. Only one Work Report may be considered for Reporting from each RcVG per block. From ddbb534780e51dff7b19b586e398c90f97da70b8 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 4 Oct 2023 13:59:21 +0200 Subject: [PATCH 21/47] Remove , introduce Beefy and state roots --- text/0031-corejam.md | 76 +++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 47 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 3ee9e6779..fb92fc410 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -95,6 +95,8 @@ mod v0 { type WorkPackageHash = [u8; 32]; struct Context { header_hash: HeaderHash, + state_root: Hash, // must be state root of block `header_hash` + beefy_root: Hash, // must be Beefy root of block `header_hash` prerequisite: Option, } struct WorkPackage { @@ -131,7 +133,7 @@ Though this process happens entirely in consensus, there are two main consensus A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. -While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Basic Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. +While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Initial Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. @@ -205,14 +207,17 @@ type ClassCodeHash = StorageMap; ``` ```rust +struct PackageInfo { + package_hash: WorkPackageHash, + context: Context, + authorization: Authorization, + auth_id: Option, +} type WorkOutputLen = ConstU32<4_096>; type WorkOutput = BoundedVec; fn refine( payload: WorkPayload, - authorization: Authorization, - auth_id: Option, - context: Context, - package_hash: WorkPackageHash, + package_info: PackageInfo, ) -> WorkOutput; ``` @@ -248,8 +253,8 @@ fn apply_refine(item: WorkItem) -> WorkResult; The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. ```rust -/// Secure refrence to a Work Package. -struct WorkPackageSpec { +/// Secure identifier for a Work Package. +struct WorkPackageId { /// The hash of the encoded `EncodedWorkPackage`. hash: WorkPackageHash, /// The erasure root of the encoded `EncodedWorkPackage`. @@ -261,7 +266,7 @@ struct WorkPackageSpec { /// of its Work Items. struct WorkReport { /// The specification of the underlying Work Package. - package_spec: WorkPackageSpec, + package_id: WorkPackageId, /// The context of the underlying Work Package. context: Context, /// The Core index under which the Work Package was Refined to generate the Report. @@ -288,15 +293,15 @@ The process continues once the Attestations arrive at the Relay-chain Block Auth ### Join-Accumulate -Join-Accumulate is a second major stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. +Join-Accumulate is the second major stage of computation and is independent from Collect-Refine. Unlike with the computation in Collect-Refine which happens contemporaneously within one of many isolated cores, the consensus computation of Join-Accumulate is both entirely synchronous with all other computation of its stage and operates within (and has access to) the same shared state-machine. -Being *on-chain* (rather than *in-core* as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out (initially) by the block-author and the resultant block evaluated by all validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. +Being *on-chain* (rather than *in-core* as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out (initially) by the Block Author and the resultant block evaluated by all Validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. #### Reporting and Integration -There are two main phases of on-chain logic before a Work Package's ramifications are irreversibly assimilated into the state of the (current fork of the) Relay-chain. The first is where the Work Package is *Registered* on-chain. This is proposed through an extrinsic introduced by the RcBA and implies the successful outcome of some *Initial Validation* (described next). This kicks-off an off-chain process of *Availability* which, if successful, culminates in a second extrinsic being introduced on-chain shortly afterwards specifying that the Availability requirements of the Work Report are met. +There are two main phases of on-chain logic before a Work Package's ramifications are irreversibly assimilated into the state of the (current fork of the) Relay-chain. The first is where the Work Package is *Reported* on-chain. This is proposed through an extrinsic introduced by the RcBA and implies the successful outcome of some *Initial Validation* (described next). This kicks-off an off-chain process of *Availability* which, if successful, culminates in a second extrinsic being introduced on-chain shortly afterwards specifying that the Availability requirements of the Work Report are met. Since this is an asynchronous process, there are no ordering guarantees on Work Reports' Availability requirements being fulfilled. There may or may not be provision for adding further delays at this point to ensure that Work Reports are processed according to strict ordering. See *Work Package Ordering*, later, for more discussion here. @@ -314,30 +319,32 @@ Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as h const RECENT_BLOCKS: u32 = 16; ``` -Thirdly, the RcBA may not attempt to Report multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. +Thirdly, dependent elements of the Context (`context.state_root` and `context.beefy_root`) must correctly correspond to those on-chain for the block corresponding to the provided `context.header_hash`. For this to be possible, the Relay-chain is expected to track Recent state roots and beefy roots in a queue. -Finally, the RcBA may not register Work Reports whose prerequisite is not itself Registered in *Recent* blocks. +Fourthly, the RcBA may not attempt to Report multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. -In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Registrations* storage item, which retaining all Work Package hashes which were Registered in the *Recent* blocks: +Finally, the RcBA may not register Work Reports whose prerequisite is not itself Reported in *Recent* blocks. + +In order to ensure all of the above tests are honoured by the RcBA, a block which contains Work Reports which fail any of these tests shall panic on import. The Relay-chain's on-chain logic will thus include these checks in order to ensure that they are honoured by the RcBA. We therefore introduce the *Recent Reports* storage item, which retaining all Work Package hashes which were Reported in the *Recent* blocks: ```rust const MAX_CORES: u32 = 512; /// Must be ordered. -type InclusionSet = BoundedVec>; -type RecentInclusions = StorageValue>> +type ReportSet = BoundedVec>; +type RecentReports = StorageValue>> ``` -The RcBA must keep an up to date set of which Work Packages have already been Registered in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. +The RcBA must keep an up to date set of which Work Packages have already been Reported in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. While it will generally be the case that RcVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RcVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: 1. it stops being *Recent*; -2. it becomes Registered on-chain; or -3. some other Attestation of the same Work Package becomes Registered on-chain. +2. it becomes Reported on-chain; or +3. some other Attestation of the same Work Package becomes Reported on-chain. #### Availability -Once the Work Report of a Work Package is Registered on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. +Once the Work Report of a Work Package is Reported on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. Only once the a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. @@ -379,34 +386,9 @@ total_weight_requirement <= weight_per_package Because of this, Work Report builders must be aware of any upcoming alterations to `max_cores` and build Statements which are in accordance with it not at present but also in the near future when it may have changed. -#### Join and `prune` - -_For consideration: Place a hard limit on total weight able to be used by `prune` in any Work Package since it is normally computed twice and an attacker can force it to be computed a third time._ - -_For consideration: Remove `prune` altogether now that we have the Basic Validity Checks._ - -The main difference between code called in the Join stage and that in the Accumulate stage is that in the former code is required to exit successfully, within the weight limit and may not mutate any state. - -The Join stage involves the Relay-chain Block Author gathering together all Work Packages backed by a Validator Group in a manner similar to the current system of PoV candidates. The Work Results are grouped according to their Work Class, and the untrusted Work Class function `prune` is called once for each such group. This returns a `Vec` of invalid indices. Using this result, invalid Work Packages may be pruned and the resultant set retried with confidence that the result will be an empty `Vec`. - -```rust -fn prune(outputs: Vec) -> Vec; -``` - -The call to the `prune` function is allocated weight equal to the length of `outputs` multiplied by the `prune` field of the Work Class's weight requirements. - -The `prune` function is used by the Relay-chain Block Author in order to ensure that all Work Packages which make it through the Join stage are non-conflicting and valid for the present Relay-chain state. All Work Packages are rechecked using the same procedure on-chain and the block is considered malformed (i.e. it panics) if the result is not an empty `Vec`. - -The `prune` function has immutable access to the Work Class's child trie state, as well as regular read-only storage access to the Relay-chain's wider state. - -```rust -fn get_work_storage(key: &[u8]) -> Result>; -fn get_work_storage_len(key: &[u8]); -``` - -#### Accumulate +### Accumulate -The second stage is that of Accumulate. This function is called on-chain only *after* the availability process has completed. +The next phase, which happens on-chain, is Accumulate. This governs the amalgamation of the Work Package Outputs calculated during the Refinement stage into the Relay-chain's overall state and in particular into the various Child Tries of the Work Classes whose Items were refined. Crucially, since the Refinement happened in-core, and since all in-core logic must be disputable and therefore its inputs made *Available* for all future disputers, Accumulation of a Work Package may only take place *after* the Availability process for it has completed. The function signature to the `accumulate` entry-point in the Work Class's code blob is: From 3d6859d030549d43615ec91613a766c50d391a3e Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 4 Oct 2023 14:01:57 +0200 Subject: [PATCH 22/47] Update text/0031-corejam.md Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 7c10f59b4..f7aa53a6d 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -143,7 +143,7 @@ Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package ### Collect-Refine -The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages collections of items together with an authorization to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Work Class's) state which happens in the following stages. +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Work Class's) state which happens in the following stages. #### Collection and `is_authorized` From e4110e64bd4795808d863763acf93dae6e3debd9 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 13 Oct 2023 14:34:58 +0100 Subject: [PATCH 23/47] Update text/0031-corejam.md Co-authored-by: gupnik <17176722+gupnik@users.noreply.github.com> --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index f7aa53a6d..f94bad4c0 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -54,7 +54,7 @@ In order of importance: The current specification of the Polkadot protocol, and with in the Relay-chain operation, is designed in line with the overall requirements of and terminology in the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol. To avoid confusion, this design will be known as the *Fixed-function Parachains Legacy Model*, or the *Legacy Model* for short. -Existing functionality relied upon by parachains will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. Transition of Polkadot to be in line with the present proposal will be necessarily imply some minor alterations of formats utilized by Cumulus, Smoldot and other light-client APIs (see the section on Compatibility). However, much of the underlying logic (in particular, consensus, disputes and availability) is retained, though its application is generalised. This proposal will only make note of the expectations regarding the changes, and presumes continuation of all other logic. +Existing functionality relied upon by parachains will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. Transition of Polkadot to be in line with the present proposal will necessarily imply some minor alterations of formats utilized by Cumulus, Smoldot and other light-client APIs (see the section on Compatibility). However, much of the underlying logic (in particular, consensus, disputes and availability) is retained, though its application is generalised. This proposal will only make note of the expectations regarding the changes, and presumes continuation of all other logic. As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Work Class Trie*. From 85db6ec84bcc9d145e5cfe0fc3ef6a2a63ccecaf Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 13 Oct 2023 14:35:17 +0100 Subject: [PATCH 24/47] Update text/0031-corejam.md Co-authored-by: gupnik <17176722+gupnik@users.noreply.github.com> --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index f94bad4c0..283e50548 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -121,7 +121,7 @@ impl TryFrom for v0::WorkPackage { A *Work Package* is an *Authorization* together with a series of *Work Items* and a context, limited in plurality, versioned and with a maximum encoded size. The Context includes an optional reference to a Work Package (`WorkPackageHash`) which limits the relative order of the Work Package (see **Work Package Ordering**, later). -(The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages which a prerequisite which is within another's sequence.) +(The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages with a prerequisite which is within another's sequence.) Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). From eb93fdafd4b8b8c4d5a68c30e9b51ff270cf4792 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 13 Oct 2023 14:37:59 +0100 Subject: [PATCH 25/47] Update text/0031-corejam.md Co-authored-by: gupnik <17176722+gupnik@users.noreply.github.com> --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 283e50548..2829ace5b 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -173,7 +173,7 @@ The `code_hash` of the Authorizer is assumed to be the hash of some code accessi fn is_authorized(param: &AuthParam, package: &WorkPackage, core_index: CoreIndex) -> bool; ``` -If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. +If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent Relay-chain state. A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. From d6bb203ae62a4457e88b52f3a22d21efd96f2275 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 13 Oct 2023 14:38:08 +0100 Subject: [PATCH 26/47] Update text/0031-corejam.md Co-authored-by: gupnik <17176722+gupnik@users.noreply.github.com> --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 2829ace5b..5e9717209 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -339,7 +339,7 @@ While it will generally be the case that RcVGs know precisely which Work Reports Once the Work Report of a Work Package is Registered on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. -Only once the a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. +Only once a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. #### Weight Provisioning From 1cb5f0d91b8021b7c2b8c3ed8a3f687f453cd833 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 16 Oct 2023 19:23:18 +0100 Subject: [PATCH 27/47] Update text/0031-corejam.md Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 5e9717209..bfc9a0f9b 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -337,7 +337,7 @@ While it will generally be the case that RcVGs know precisely which Work Reports #### Availability -Once the Work Report of a Work Package is Registered on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package make take one or more blocks to be made Available or may even fail. +Once the Work Report of a Work Package is Registered on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package may take one or more blocks to be made Available or may even fail. Only once a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. From 95987d3b064eb3152f28e759279008e120ea451a Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 16 Oct 2023 19:23:43 +0100 Subject: [PATCH 28/47] Update text/0031-corejam.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index bfc9a0f9b..0ff163081 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -131,7 +131,7 @@ Though this process happens entirely in consensus, there are two main consensus A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. -While a Work Package is being built, the *Builder* must have a synchronized Relay-chain node in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Basic Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. +While a Work Package is being built, the *Builder* must have access to the Relay-chain state in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Basic Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. From d87f686845dfb4b0a0b77c0a10dd2af56448615d Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 16 Oct 2023 19:27:40 +0100 Subject: [PATCH 29/47] Update text/0031-corejam.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0031-corejam.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 0ff163081..745e11dff 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -250,11 +250,11 @@ The amount of weight used in executing the `refine` function is noted in the `Wo ```rust /// Secure refrence to a Work Package. struct WorkPackageSpec { - /// The hash of the encoded `EncodedWorkPackage`. + /// The hash of the SCALE encoded `EncodedWorkPackage`. hash: WorkPackageHash, - /// The erasure root of the encoded `EncodedWorkPackage`. + /// The erasure root of the SCALE encoded `EncodedWorkPackage`. root: ErasureRoot, - /// The length in bytes of encoded `EncodedWorkPackage`. + /// The length in bytes of SCALE encoded `EncodedWorkPackage`. len: u32, } /// Execution report of a Work Package, mainly comprising the Results from the Refinement From d64f5686292650a7cdfedfcd3ad28d817235bb27 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 16 Oct 2023 19:40:55 +0100 Subject: [PATCH 30/47] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0031-corejam.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 745e11dff..d9036244a 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -277,7 +277,7 @@ struct Attestation { /// Which validators from the group have a signature in `attestations`. validators: BitVec, /// The signatures of the RcVG members set out in `validators` whose message is the - /// hash of the `report`. + /// hash of the `report`. The order of the signatures is the same order as the validators appear in `validators`. attestations: Vec, } ``` @@ -314,7 +314,7 @@ Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as h const RECENT_BLOCKS: u32 = 16; ``` -Thirdly, the RcBA may not attempt to Report multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. +Thirdly, the RcBA may not attempt to report multiple Work Reports for the same Work Package. Since Work Reports become inherently invalid once they are no longer *Recent*, then this check may be simplified to ensuring that there are no Work Reports of the same Work Package within any *Recent* blocks. Finally, the RcBA may not register Work Reports whose prerequisite is not itself Registered in *Recent* blocks. @@ -339,7 +339,7 @@ While it will generally be the case that RcVGs know precisely which Work Reports Once the Work Report of a Work Package is Registered on-chain, the Work Package itself must be made *Available* through the off-chain Availability Protocol, which ensures that any dispute over the correctness of the Work Report can be easily objectively judged by all validators. Being off-chain this is not block-synchronized and any given Work Package may take one or more blocks to be made Available or may even fail. -Only once a Work Report's Work Package is made Available can the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. +Only once a Work Report's Work Package is made Available the processing continue with the next steps of Joining and Accumulation. Ordering requirements of Work Packages may also affect this variable latency and this is discussed later in the section **Work Package Ordering**. #### Weight Provisioning @@ -435,7 +435,7 @@ fn remove_work_storage(key: &[u8]); fn ump_enqueue(message: &[u8]) -> Result<(), ()>; ``` -Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_storage` may fail if an insufficient deposit is held under the Work Class's account. +Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Work Class's account. UMP messages for the Relay-chain to interpret may be enqueued through `ump_enqueue` function. For this to succeed, the Relay-chain must have pre-authorized the use of UMP for this endpoint. From a1eaff8582d874ee3c63fff7e64df0d27befae8b Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 17 Oct 2023 11:56:19 +0100 Subject: [PATCH 31/47] tweaks --- text/0031-corejam.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index fb92fc410..7e2d294ea 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -4,7 +4,7 @@ | --------------- | ------------------------------------------------------------------------------------------- | | **Start Date** | 11 September 2023 | | **Description** | Parallelised, decentralised, permissionless state-machine based on a multistage Collect-Refine-Join-Accumulate model. | -| **Authors** | Gavin Wood, Robert Habermeier, Bastian Köcher | +| **Authors** | Gavin Wood, Robert Habermeier, Bastian Köcher, Alistair Stewart | ## Summary @@ -85,7 +85,6 @@ mod v0 { payload: WorkPayload, } type MaxWorkItemsInPackage = ConstU32<4>; - type MaxWorkPackagePrerequisites = ConstU32<4>; enum Authorization { Instantaneous(InstantaneousAuth), Bulk(Vec), @@ -175,7 +174,7 @@ The `code_hash` of the Authorizer is assumed to be the hash of some code accessi fn is_authorized(param: &AuthParam, package: &WorkPackage, core_index: CoreIndex) -> bool; ``` -If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with a `context` parameter in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. +If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with the package's `context` field in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent state Relay-chain state. A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. From 52fa11201f8bef5b3743e3b3fa56305e147db3bf Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 18 Oct 2023 07:58:06 +0100 Subject: [PATCH 32/47] Add host function `transfer` --- text/0031-corejam.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index c9ba279a6..4624721fd 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -222,13 +222,11 @@ fn refine( Both `refine` and `is_authorized` are only ever executed in-core. Within this environment, we need to ensure that we can interrupt computation not long after some well-specified limit and deterministically determine when an invocation of the VM exhausts this limit. Since the exact point at which interruption of computation need not be deterministic, it is expected to be executed by a streaming JIT transpiler with a means of approximate and overshooting interruption coupled with deterministic metering. -Several host functions (largely in line with the host functions available to Parachain Validation Function code) are supplied. Two additional ones include: +Several host functions (largely in line with the host functions available to Parachain Validation Function code) are supplied. One addition is: ```rust /// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. fn lookup(hash: [u8; 32]) -> Vec; -/// Determine the state root of the block at given `height`. -fn state_root(height: u32) -> Option<[u8; 32]>; ``` Other host functions will allow for the possibility of executing a WebAssembly payload (for example, a Parachain Validation Function) or instantiating and entering a subordinate RISCV VM (for example for Actor Progressions). @@ -414,12 +412,15 @@ fn weight_remaining() -> Weight; fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; fn remove_work_storage(key: &[u8]); fn ump_enqueue(message: &[u8]) -> Result<(), ()>; +fn transfer(destination: WorkClass, amount: u128, memo: &[u8]) -> Result, ()>; ``` Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Work Class's account. UMP messages for the Relay-chain to interpret may be enqueued through `ump_enqueue` function. For this to succeed, the Relay-chain must have pre-authorized the use of UMP for this endpoint. +Simple transfers of data and balance between Work Classes are possible by the `transfer` function. This is an entirely synchronous function which transfers the execution over to a `destination` Work Class as well as the provided `amount`. During this execution, no `checkpoint()`ing is permitted. The operation may result in error in which case all changes to state are reverted, including the balance transfer. (Weight is still used.) Since this generally requires the sending Work Class to trust the receiver not to burn their Weight, transfers will probably happen in a semi-federated fashion, with trusted hubs forming as clearing houses, both to avert any possibility of Weight misuse and to move most transfers in-core. + Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `accumulate` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). From 26ed3f28a2364d74b9c6ef57a8b67ea1fe21073b Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 18 Oct 2023 08:09:51 +0100 Subject: [PATCH 33/47] Smart contract stuff --- text/0031-corejam.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 4624721fd..855b0807f 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -412,19 +412,27 @@ fn weight_remaining() -> Weight; fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; fn remove_work_storage(key: &[u8]); fn ump_enqueue(message: &[u8]) -> Result<(), ()>; -fn transfer(destination: WorkClass, amount: u128, memo: &[u8]) -> Result, ()>; +fn transfer(destination: WorkClass, amount: u128, memo: &[u8], weight: Weight) -> Result, ()>; ``` Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Work Class's account. UMP messages for the Relay-chain to interpret may be enqueued through `ump_enqueue` function. For this to succeed, the Relay-chain must have pre-authorized the use of UMP for this endpoint. -Simple transfers of data and balance between Work Classes are possible by the `transfer` function. This is an entirely synchronous function which transfers the execution over to a `destination` Work Class as well as the provided `amount`. During this execution, no `checkpoint()`ing is permitted. The operation may result in error in which case all changes to state are reverted, including the balance transfer. (Weight is still used.) Since this generally requires the sending Work Class to trust the receiver not to burn their Weight, transfers will probably happen in a semi-federated fashion, with trusted hubs forming as clearing houses, both to avert any possibility of Weight misuse and to move most transfers in-core. - Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `accumulate` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). +Simple transfers of data and balance between Work Classes are possible by the `transfer` function. This is an entirely synchronous function which transfers the execution over to a `destination` Work Class as well as the provided `amount` into their account. + +A new VM is set up with code according to the Work Class's `accumulate` code blob, but with a secondary entry point whose prototype is: + +```rust +fn on_transfer(source: WorkClass, amount: u128, memo: Vec) -> Result, ()>; +``` + +During this execution, all host functions above may be used except `checkpoint()`. The operation may result in error in which case all changes to state are reverted, including the balance transfer. (Weight is still used.) + Other host functions, including some to access Relay-chain hosted services such as the Balances and Storage Pallet may also be provided commensurate with this executing on-chain. _(Note for discussion: Should we be considering light-client proof size at all here?)_ From f0dea5e96ce0e9497b0d8e7c7c884c17d1ef0194 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 18 Oct 2023 15:56:56 +0100 Subject: [PATCH 34/47] Ensure that context is checked prior to is_authorized --- text/0031-corejam.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 855b0807f..0ec1bdbc9 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -150,6 +150,8 @@ The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. +On arrival of a Work Package, after the initial decoding, a first check is the that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. + There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. RcVGs run the risk of a credit owner not having the credit at the point of eventual payment (in the Join stage, later), in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RcVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. From 62436a6dc322491fba9b05c1100e9fa671f9a038 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 18 Oct 2023 23:39:13 +0100 Subject: [PATCH 35/47] fix --- text/0031-corejam.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 0ec1bdbc9..d10f49a44 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -140,7 +140,7 @@ The second consensus computation happens on-chain at the behest of the Relay-cha At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. At this point we can say that the Work Report's Package is *Available*. -Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Work Classes, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `a` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **Work Package Ordering**, later). +Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Work Classes, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `o` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **Work Package Ordering**, later). ### Collect-Refine @@ -413,13 +413,25 @@ fn checkpoint() -> Weight; fn weight_remaining() -> Weight; fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; fn remove_work_storage(key: &[u8]); -fn ump_enqueue(message: &[u8]) -> Result<(), ()>; -fn transfer(destination: WorkClass, amount: u128, memo: &[u8], weight: Weight) -> Result, ()>; +fn set_validators(validator_keys: &[ValidatorKey]) -> Result<(), ()>; +fn set_code(code: &[u8]) -> Result<(), ()>; +fn assign_core( + core: CoreIndex, + begin: BlockNumber, + assignment: Vec<(CoreAssignment, PartsOf57600)>, + end_hint: Option, +) -> Result<(), ()>; +fn transfer( + destination: WorkClass, + amount: u128, + memo: &[u8], + weight: Weight, +) -> Result, ()>; ``` Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Work Class's account. -UMP messages for the Relay-chain to interpret may be enqueued through `ump_enqueue` function. For this to succeed, the Relay-chain must have pre-authorized the use of UMP for this endpoint. +`set_validator`, `set_code` and `assign_core` are all privileged operations and may only be called by pre-authorized Work Classes. Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `accumulate` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. @@ -578,13 +590,13 @@ Actor code is stored in the Storage Pallet. Actor-specific data including code h At present, both HRMP (the stop-gap measure introduced in lieu of proper XCMP) and UMP, make substantial usage of the ability for parachains to include data in their PoV which will be interpreted on-chain by the Relay-chain. The current limit of UMP alone is 1MB. Even with the current amount of parachains, it is not possible for all parachains to be able to make use of these resources within the same block, and the difficult problem of apportioning resources in the case of contest is structurally unsolved and left for the RcBA to make an arbitrary selection. -The present proposal aims to bring some clarity to this situation by limiting the amount of data which can arrive on the Relay-chain from each Work Item, and by extension from each Work Package. The specific limit proposed is 4KB per Work Item, which if we assume an average of two Work Items per Package and 250 cores, comes to a manageable 2MB and leaves plenty of headroom. +The present proposal brings soundness to this situation by limiting the amount of data which can arrive on the Relay-chain from each Work Item, and by extension from each Work Package. The specific limit proposed is 4KB per Work Item, which if we assume an average of two Work Items per Package and 250 cores, comes to a manageable 2MB and leaves plenty of headroom. -However, this does mean that pre-existing usage of UMP and HRMP would be substanially impaired. For example, under these limits a future Staking System-chain would be unable to send 1,000 sets of validator keys (each taking up to around 100 bytes) to the Relay-chain within a single block. +However, this does mean that pre-existing usage of UMP and HRMP are impossible. In any case, UMP is removed entirely from the Work Class API. -The overall situation will be improved substantially through preexisting plans, particularly the development and deployment of XCMP to avoid the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. The "Hermit-Relay" concept of spinning out ancilliary functionality handled by the Relay-chain to System chains will also alleviate the situation somewhat. Taken together, HRMP will no longer exist and UMP will be limited to a few System-chains making well-bounded interactions. +To make up for this change, all non-"kernel" Relay-chain functionality will exist within Work Classes (parachains under CoreChains, or possibly even actors under CorePlay). This includes staking and governance functionality. The development and deployment of XCMP avoids the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. APIs are provided for the few operations remaining which the Relay-chain must provide (validator updates, code updates and core assignments) but may only be used by Work Classes holding the appropriate privileges. Thus taken together, neither HRMP, UMP or DMP will exist. -An initial deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Work Class to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. +An initial and hybrid deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Work Class to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. ### Notes on Implementation Order From 8b321a7a3d3b3cf70ce4f2992b3e636e3f9a5d71 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 18 Oct 2023 23:40:05 +0100 Subject: [PATCH 36/47] Update text/0031-corejam.md Co-authored-by: Oliver Tale-Yazdi --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index d10f49a44..e272b3207 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -144,7 +144,7 @@ Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package ### Collect-Refine -The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield a *Work Result*. Finally, each Validator Group member attests to a Work Package yielding a set of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Work Class's) state which happens in the following stages. +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield *Work Results*. Finally, each Validator Group member attests to a Work Package yielding a series of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Work Class's) state which happens in the following stages. #### Collection and `is_authorized` From e32333dfc4626ef16417f524e76bdb6dcd3d3652 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 18 Oct 2023 23:40:18 +0100 Subject: [PATCH 37/47] Update text/0031-corejam.md Co-authored-by: Oliver Tale-Yazdi --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index e272b3207..8ab6523e5 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -150,7 +150,7 @@ The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. -On arrival of a Work Package, after the initial decoding, a first check is the that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. +On arrival of a Work Package, after the initial decoding, a first check is that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. From e33e467400419621ca5f47e359d294bef6674822 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Wed, 18 Oct 2023 23:40:33 +0100 Subject: [PATCH 38/47] Update text/0031-corejam.md Co-authored-by: Oliver Tale-Yazdi --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 8ab6523e5..2d9789922 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -152,7 +152,7 @@ Collection is the means of a Validator Group member attaining a Work Package whi On arrival of a Work Package, after the initial decoding, a first check is that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. -There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. +There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which owns enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate by signing a payload of the Work Package hash. RcVGs run the risk of a credit owner not having the credit at the point of eventual payment (in the Join stage, later), in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RcVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. From 4d457697cab43c5bbd3fc40ab9199af56e641fa4 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 20 Oct 2023 10:53:12 +0100 Subject: [PATCH 39/47] No more ICT --- text/0031-corejam.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index d10f49a44..40d7de868 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -85,10 +85,7 @@ mod v0 { payload: WorkPayload, } type MaxWorkItemsInPackage = ConstU32<4>; - enum Authorization { - Instantaneous(InstantaneousAuth), - Bulk(Vec), - } + type Authorization = Vec; type HeaderHash = [u8; 32]; /// Just a Blake2-256 hash of an EncodedWorkPackage. type WorkPackageHash = [u8; 32]; @@ -152,11 +149,9 @@ Collection is the means of a Validator Group member attaining a Work Package whi On arrival of a Work Package, after the initial decoding, a first check is the that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. -There are two kinds of Authorization corresponding to the two kinds of Coretime which are sold by Polkadot (see RFC#0001). An Authorization for usage of Instantaneous Coretime consists of a self-contained Signature of an account which own enough Instantaneous Coretime Credit in order to purchase a block of Coretime at the current rate signing a payload of the Work Package hash. - -RcVGs run the risk of a credit owner not having the credit at the point of eventual payment (in the Join stage, later), in which case the RcVG will not be rewarded. Credit may never be withdrawn, therefore RcVGs can safely accept a block if and only if the Credit account contains a balance of at least the product of the number of Cores assigned to IC, the price per IC core per block and the number of blocks behind the head of the finalized chain which the RcVG currently may be. +Agile Coretime (see RFC#0001) prescribes two forms of Coretime sales: Instantaneous and Bulk. Sales of Instantaneous Coretime are no longer provided, leaving only Bulk Coretime. -An Authorization for usage of Bulk Coretime is more sophisticated. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. +We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. An *Authorizer* is a parameterized procedure: From 9360c196cf2d75aacbe89c93b62ed92d9e5cd0f0 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 21 Oct 2023 09:11:21 +0200 Subject: [PATCH 40/47] Update 0031-corejam.md Co-authored-by: Xiliang Chen --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index fc18ba8dd..1acacd4c7 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -137,7 +137,7 @@ The second consensus computation happens on-chain at the behest of the Relay-cha At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. At this point we can say that the Work Report's Package is *Available*. -Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Work Classes, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `o` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **Work Package Ordering**, later). +Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Work Classes, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `o` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **[Work Package Ordering](#work-package-ordering)**, later). ### Collect-Refine From 23470a27ec4863b28863de5e3961870639572262 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sat, 21 Oct 2023 09:11:29 +0200 Subject: [PATCH 41/47] Update 0031-corejam.md Co-authored-by: Xiliang Chen --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 1acacd4c7..8ffe05bfc 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -149,7 +149,7 @@ Collection is the means of a Validator Group member attaining a Work Package whi On arrival of a Work Package, after the initial decoding, a first check is that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. -Agile Coretime (see RFC#0001) prescribes two forms of Coretime sales: Instantaneous and Bulk. Sales of Instantaneous Coretime are no longer provided, leaving only Bulk Coretime. +Agile Coretime (see [RFC#0001](https://github.com/polkadot-fellows/RFCs/blob/main/text/0001-agile-coretime.md)) prescribes two forms of Coretime sales: Instantaneous and Bulk. Sales of Instantaneous Coretime are no longer provided, leaving only Bulk Coretime. We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. From 7d34d4a7192c8ad8ca33ab75e91ada8ff6db0fbf Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 23 Oct 2023 12:35:34 +0100 Subject: [PATCH 42/47] Explanation --- text/0031-corejam.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index fc18ba8dd..bdd6abe72 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -50,9 +50,16 @@ In order of importance: **CoreJam is a general model for utilization of Polkadot Cores. It is a mechanism by which Work Packages are communicated, authorized, computed and verified, and their results gathered, combined and accumulated into particular parts of the Relay-chain's state.** +### Terminology + +Short forms of several common term are used here for brevity: + +- *RcVG*: Relay-chain Validator Group +- *RcBA*: Relay-chain Block Author + ### From Old to New -The current specification of the Polkadot protocol, and with in the Relay-chain operation, is designed in line with the overall requirements of and terminology in the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol. To avoid confusion, this design will be known as the *Fixed-function Parachains Legacy Model*, or the *Legacy Model* for short. +The current specification of the Polkadot protocol, and with in the Relay-chain operation, is designed in line with the overall requirements of and terminology in the Polkadot (1.0) whitepaper. It incorporates first-class concepts including *Proof-of-Validity* and *Parachain Validation Function*. This will no longer be considered canonical for the Polkadot protocol. To avoid confusion, this design will be known as the *Fixed-function Parachains Model*, or the *Parachains Model* for short. Existing functionality relied upon by parachains will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. Transition of Polkadot to be in line with the present proposal will necessarily imply some minor alterations of formats utilized by Cumulus, Smoldot and other light-client APIs (see the section on Compatibility). However, much of the underlying logic (in particular, consensus, disputes and availability) is retained, though its application is generalised. This proposal will only make note of the expectations regarding the changes, and presumes continuation of all other logic. @@ -121,7 +128,13 @@ A *Work Package* is an *Authorization* together with a series of *Work Items* an (The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages with a prerequisite which is within another's sequence.) -Work Items are a pair of class and payload, where the `class` identifies the Class of Work to be done in this item (*Work Class*). +Work Items are a pair of class and payload, where the `class` identifies a pairing of code and state known as a *Work Class* and `payload` is a block of data which, through the aforementioned code, mutates said state in some presumably useful way. + +A Work Class has certain similarities to an object in a decentralized object-oriented execution environment (or, indeed, a smart contract), with the main difference being a more exotic computation architecture available to it. Similar to smart contracts, a Work Class's state is stored on-chain and transitioned only using on-chain logic. Also similar to a smart contract, resources used by a Work Class are strictly and deterministically constrained (using dynamic metering). Finally, Work Classes, like smart contracts, are able to hold funds and call into each other synchronously. + +However, unlike for smart contracts, the on-chain transition logic of a Work Class (known as the *Accumulate* function) cannot directly be interacted with by actors external to the consensus environment. Concretely, they cannot be transacted with. Aside from the aforementioned inter-class calling, all input data (and state progression) must come as the result of a Work Item. A Work Item is a blob of data meant for a particular Work Class and crafted by some source external to consensus. It may be thought of as akin to a transaction. The Work Item is first processed *in-core*, which is to say on one of many secure and isolated virtual decentralized processors, yielding a distillate known as a Work Result. It is this Work Result which is collated together with others of the same class and Accumulated into the Work Class. + +In short, a Work Class may be considered as a kind of smart contract albeit one whose transaction data is first preprocessed with cheap decentralized compute power. Though this process happens entirely in consensus, there are two main consensus environments at play, _in-core_ and _on-chain_. We therefore partition the progress into two pairs of stages: Collect & Refine and Join & Accumulate. From 259e466c57147c5ef143d2fc35e695efaf5d89c3 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 23 Oct 2023 12:38:44 +0100 Subject: [PATCH 43/47] Rename Work Class -> Service --- text/0031-corejam.md | 108 +++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index bdd6abe72..b678167c0 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -25,7 +25,7 @@ More recently, the idea of having small to medium size programs executing withou Beyond delivering additional value through the increased potential for use-cases that this flexibility allows, our motivation extends to gaining stability: a future-proof platform allowing teams to build on it without fear of high maintenance burden, continuous bitrot or a technological rug-pull at some later date. Secondly, we are motivated by reducing barriers for new teams, allowing the Polkadot platform to harness the power of the crowd which permissionless systems uniquely enable. -Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the classical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. +Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the serviceical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. ## Requirements @@ -63,7 +63,7 @@ The current specification of the Polkadot protocol, and with in the Relay-chain Existing functionality relied upon by parachains will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. Transition of Polkadot to be in line with the present proposal will necessarily imply some minor alterations of formats utilized by Cumulus, Smoldot and other light-client APIs (see the section on Compatibility). However, much of the underlying logic (in particular, consensus, disputes and availability) is retained, though its application is generalised. This proposal will only make note of the expectations regarding the changes, and presumes continuation of all other logic. -As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Work Class*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Work Class Trie*. +As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Service*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Service Trie*. Focussing on continuity and reuse of existing logic, it is unsurprising that many of these concepts already analogues in the Parachains model, albeit ones with a less general definition. While this mapping can be helpful to quickly create an approximate understanding of the new concepts for those already familiar with Polkadot, care must be taken not to inadvertantly make incorrect presumptions over exact details of their relationships, constraints, timing, provisions and APIs. Nonetheless, they are provided here for whatever help they may be. @@ -78,17 +78,17 @@ Focussing on continuity and reuse of existing logic, it is unsurprising that man | *Integration* | Inclusion | Irreversible transition of state | | *Builder* | Collator | Creator of data worthy of Attestation | -Additionally, the *Work Class Trie* has no immediate analogue, but may be considered as the Relay-chain state used to track the code and head data of the parachains. +Additionally, the *Service Trie* has no immediate analogue, but may be considered as the Relay-chain state used to track the code and head data of the parachains. ### Overview ```rust mod v0 { const PROGRESS_WEIGHT_PER_PACKAGE: Weight = MAX_BLOCK_WEIGHT * 3 / 4; - type WorkClass = u32; + type Service = u32; type WorkPayload = Vec; struct WorkItem { - class: WorkClass, + service: Service, payload: WorkPayload, } type MaxWorkItemsInPackage = ConstU32<4>; @@ -128,19 +128,19 @@ A *Work Package* is an *Authorization* together with a series of *Work Items* an (The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages with a prerequisite which is within another's sequence.) -Work Items are a pair of class and payload, where the `class` identifies a pairing of code and state known as a *Work Class* and `payload` is a block of data which, through the aforementioned code, mutates said state in some presumably useful way. +Work Items are a pair of service and payload, where the `service` identifies a pairing of code and state known as a *Service* and `payload` is a block of data which, through the aforementioned code, mutates said state in some presumably useful way. -A Work Class has certain similarities to an object in a decentralized object-oriented execution environment (or, indeed, a smart contract), with the main difference being a more exotic computation architecture available to it. Similar to smart contracts, a Work Class's state is stored on-chain and transitioned only using on-chain logic. Also similar to a smart contract, resources used by a Work Class are strictly and deterministically constrained (using dynamic metering). Finally, Work Classes, like smart contracts, are able to hold funds and call into each other synchronously. +A Service has certain similarities to an object in a decentralized object-oriented execution environment (or, indeed, a smart contract), with the main difference being a more exotic computation architecture available to it. Similar to smart contracts, a Service's state is stored on-chain and transitioned only using on-chain logic. Also similar to a smart contract, resources used by a Service are strictly and deterministically constrained (using dynamic metering). Finally, Servicees, like smart contracts, are able to hold funds and call into each other synchronously. -However, unlike for smart contracts, the on-chain transition logic of a Work Class (known as the *Accumulate* function) cannot directly be interacted with by actors external to the consensus environment. Concretely, they cannot be transacted with. Aside from the aforementioned inter-class calling, all input data (and state progression) must come as the result of a Work Item. A Work Item is a blob of data meant for a particular Work Class and crafted by some source external to consensus. It may be thought of as akin to a transaction. The Work Item is first processed *in-core*, which is to say on one of many secure and isolated virtual decentralized processors, yielding a distillate known as a Work Result. It is this Work Result which is collated together with others of the same class and Accumulated into the Work Class. +However, unlike for smart contracts, the on-chain transition logic of a Service (known as the *Accumulate* function) cannot directly be interacted with by actors external to the consensus environment. Concretely, they cannot be transacted with. Aside from the aforementioned inter-service calling, all input data (and state progression) must come as the result of a Work Item. A Work Item is a blob of data meant for a particular Service and crafted by some source external to consensus. It may be thought of as akin to a transaction. The Work Item is first processed *in-core*, which is to say on one of many secure and isolated virtual decentralized processors, yielding a distillate known as a Work Result. It is this Work Result which is collated together with others of the same service and Accumulated into the Service. -In short, a Work Class may be considered as a kind of smart contract albeit one whose transaction data is first preprocessed with cheap decentralized compute power. +In short, a Service may be considered as a kind of smart contract albeit one whose transaction data is first preprocessed with cheap decentralized compute power. Though this process happens entirely in consensus, there are two main consensus environments at play, _in-core_ and _on-chain_. We therefore partition the progress into two pairs of stages: Collect & Refine and Join & Accumulate. ### Processing stages of a Work Package -A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Work Class. +A Work Package has several stages of consensus computation associated with its processing, which happen as the system becomes more certain that it represents a correct and useful transition of its Service. While a Work Package is being built, the *Builder* must have access to the Relay-chain state in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Initial Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. @@ -150,11 +150,11 @@ The second consensus computation happens on-chain at the behest of the Relay-cha At some point later `T+r+i+a` (where `a` is the time to distribute the fragments of the Work Package and report their archival to the next Relay-chain Block Author) the Availability Protocol has concluded and the Relay-chain Block Author of the time brings this information on-chain in the form of a bitfield in which an entry flips from zero to one. At this point we can say that the Work Report's Package is *Available*. -Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Work Classes, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `o` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **Work Package Ordering**, later). +Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package are aggregated into groups of Servicees, and then *Pruned* and *Accumulated* into the common state of the Relay-chain. This process is known as *Integration* (in the fixed-function parachains model, this is known as "inclusion") and is irreversible within any given fork. Additional latency from being made *Available* to being *Integrated* (i.e. the `o` component) may be incurred due to ordering requirements, though it is expected to be zero in the variant of this proposal to be implemented initially (see **Work Package Ordering**, later). ### Collect-Refine -The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield *Work Results*. Finally, each Validator Group member attests to a Work Package yielding a series of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Work Class's) state which happens in the following stages. +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield *Work Results*. Finally, each Validator Group member attests to a Work Package yielding a series of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Service's) state which happens in the following stages. #### Collection and `is_authorized` @@ -164,7 +164,7 @@ On arrival of a Work Package, after the initial decoding, a first check is that Agile Coretime (see RFC#0001) prescribes two forms of Coretime sales: Instantaneous and Bulk. Sales of Instantaneous Coretime are no longer provided, leaving only Bulk Coretime. -We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Work Class. +We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Service. An *Authorizer* is a parameterized procedure: @@ -209,7 +209,7 @@ This ensures that Validators do a strictly limited amount of work before knowing ### Refine -The `refine` function is implemented as an entry-point inside a code blob which is stored on-chain and whose hash is associated with the Work Class. +The `refine` function is implemented as an entry-point inside a code blob which is stored on-chain and whose hash is associated with the Service. ```rust type ClassCodeHash = StorageMap; @@ -249,7 +249,7 @@ enum WorkError { Panic, } struct WorkResult { - class: WorkClass, + service: Service, item_hash: [u8; 32], result: Result, weight: Weight, @@ -257,7 +257,7 @@ struct WorkResult { fn apply_refine(item: WorkItem) -> WorkResult; ``` -The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Work Classes whose items appear in the Work Packages. +The amount of weight used in executing the `refine` function is noted in the `WorkResult` value, and this is used later in order to help apportion on-chain weight (for the Join-Accumulate process) to the Servicees whose items appear in the Work Packages. ```rust /// Secure refrence to a Work Package. @@ -304,7 +304,7 @@ Join-Accumulate is the second major stage of computation and is independent from Being *on-chain* (rather than *in-core* as with Collect-Refine), information and computation done in the Join-Accumulate stage is carried out (initially) by the Block Author and the resultant block evaluated by all Validators and full-nodes. Because of this, and unlike in-core computation, it has full access to the Relay-chain's state. -The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Work Class or even different Work Classes and oversee message queue transitions. +The Join-Accumulate stage may be seen as a synchronized counterpart to the parallelised Collect-Refine stage. It may be used to integrate the work done from the context of an isolated VM into a self-consistent singleton world model. In concrete terms this means ensuring that the independent work components, which cannot have been aware of each other during the Collect-Refine stage, do not conflict in some way. Less dramatically, this stage may be used to enforce ordering or provide a synchronisation point (e.g. for combining entropy in a sharded RNG). Finally, this stage may be a sensible place to manage asynchronous interactions between subcomponents of a Service or even different Servicees and oversee message queue transitions. #### Reporting and Integration @@ -312,7 +312,7 @@ There are two main phases of on-chain logic before a Work Package's ramification Since this is an asynchronous process, there are no ordering guarantees on Work Reports' Availability requirements being fulfilled. There may or may not be provision for adding further delays at this point to ensure that Work Reports are processed according to strict ordering. See *Work Package Ordering*, later, for more discussion here. -Once both Availability and any additional requirements are met (including ordering and dependencies, but possibly also including reevaluation of some of the Initial Validation checks), then the second phase is executed which is known as *Integration*. This is the irreversible application of Work Report consequences into the Work Class's State Trie and (via certain permissionless host functions) the wider state of the Relay-chain. Work Results are segregated into groups based on their Work Class, joined into a `Vec` and passed through the immutable Prune function and into the mutable Accumulate function. +Once both Availability and any additional requirements are met (including ordering and dependencies, but possibly also including reevaluation of some of the Initial Validation checks), then the second phase is executed which is known as *Integration*. This is the irreversible application of Work Report consequences into the Service's State Trie and (via certain permissionless host functions) the wider state of the Relay-chain. Work Results are segregated into groups based on their Service, joined into a `Vec` and passed through the immutable Prune function and into the mutable Accumulate function. #### Initial Validation @@ -361,17 +361,17 @@ Join-Accumulate is, as the name suggests, comprised of two subordinate stages. B Practically speaking, we may allow a similar VM execution metering system similar to that for the `refine` execution, whereby we do not require a strictly deterministic means of interrupting, but do require deterministic metering and only approximate interruption. This would mean that full-nodes and Relay-chain validators could be made to execute some additional margin worth of computation without payment, though any attack could easily be mitigated by attaching a fixed cost (either economically or in weight terms) to an VM invocation. -Each Work Class defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Report does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. +Each Service defines some requirements it has regarding the provision of on-chain weight. Since all on-chain weight requirements must be respected of all processed Work Packages, it is important that each Work Report does not imply using more weight than its fair portion of the total available, and in doing so provides enough weight to its constituent items to meet their requirements. ```rust struct WorkItemWeightRequirements { prune: Weight, accumulate: Weight, } -type WeightRequirements = StorageMap; +type WeightRequirements = StorageMap; ``` -Each Work Class has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Work Class logic and represent the amount of weight allotted for each Work Item of this class within in a Work Package assigned to a Core. +Each Service has two weight requirements associated with it corresponding to the two pieces of permissionless on-chain Service logic and represent the amount of weight allotted for each Work Item of this service within in a Work Package assigned to a Core. The total amount of weight utilizable by each Work Package (`weight_per_package`) is specified as: @@ -386,7 +386,7 @@ A Work Report is only valid if all weight liabilities of all Work Items to be Ac ```rust let total_weight_requirement = work_statement .items - .map(|item| weight_requirements[item.class]) + .map(|item| weight_requirements[item.service]) .sum(|requirements| requirements.prune + requirements.accumulate); total_weight_requirement <= weight_per_package ``` @@ -395,24 +395,24 @@ Because of this, Work Report builders must be aware of any upcoming alterations ### Accumulate -The next phase, which happens on-chain, is Accumulate. This governs the amalgamation of the Work Package Outputs calculated during the Refinement stage into the Relay-chain's overall state and in particular into the various Child Tries of the Work Classes whose Items were refined. Crucially, since the Refinement happened in-core, and since all in-core logic must be disputable and therefore its inputs made *Available* for all future disputers, Accumulation of a Work Package may only take place *after* the Availability process for it has completed. +The next phase, which happens on-chain, is Accumulate. This governs the amalgamation of the Work Package Outputs calculated during the Refinement stage into the Relay-chain's overall state and in particular into the various Child Tries of the Servicees whose Items were refined. Crucially, since the Refinement happened in-core, and since all in-core logic must be disputable and therefore its inputs made *Available* for all future disputers, Accumulation of a Work Package may only take place *after* the Availability process for it has completed. -The function signature to the `accumulate` entry-point in the Work Class's code blob is: +The function signature to the `accumulate` entry-point in the Service's code blob is: ```rust fn accumulate(results: Vec<(Authorization, Vec<(ItemHash, WorkResult)>)>); type ItemHash = [u8; 32]; ``` -The logic in `accumulate` may need to know how the various Work Items arrived into a processed Work Package. Since a Work Package could have multiple Work Items of the same Work Class, it makes sense to have a separate inner `Vec` for Work Items sharing the Authorization (by virtue of being in the same Work Package). +The logic in `accumulate` may need to know how the various Work Items arrived into a processed Work Package. Since a Work Package could have multiple Work Items of the same Service, it makes sense to have a separate inner `Vec` for Work Items sharing the Authorization (by virtue of being in the same Work Package). Work Items are identified by their Blake2-256 hash, known at the *Item Hash* (`ItemHash`). We provide both the Authorization of the Package and the constituent Work Item Hashes and their Results in order to allow the `refine` logic to take appropriate action in the case that an invalid Work Item was submitted (i.e. one which caused its Refine operation to panic or time-out). _(Note for later: We may wish to provide a more light-client friendly Work Item identifier than a simple hash; perhaps a Merkle root of equal-size segments.)_ -There is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Work Class's weight requirements. +There is an amount of weight which it is allowed to use before being forcibly terminated and any non-committed state changes lost. The lowest amount of weight provided to `accumulate` is defined as the number of `WorkResult` values passed in `results` to `accumulate` multiplied by the `accumulate` field of the Service's weight requirements. -However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Work Classes of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by Classes within one Package may be carried over to the next Package and utilized there. +However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Servicees of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by Classes within one Package may be carried over to the next Package and utilized there. ```rust fn get_work_storage(key: &[u8]) -> Option>; @@ -430,27 +430,27 @@ fn assign_core( end_hint: Option, ) -> Result<(), ()>; fn transfer( - destination: WorkClass, + destination: Service, amount: u128, memo: &[u8], weight: Weight, ) -> Result, ()>; ``` -Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Work Class's account. +Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Service's account. -`set_validator`, `set_code` and `assign_core` are all privileged operations and may only be called by pre-authorized Work Classes. +`set_validator`, `set_code` and `assign_core` are all privileged operations and may only be called by pre-authorized Servicees. -Full access to a child trie specific to the Work Class is provided through the `work_storage` host functions. Since `accumulate` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Work Class to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. +Full access to a child trie specific to the Service is provided through the `work_storage` host functions. Since `accumulate` is permissionless and untrusted code, we must ensure that its child trie does not grow to degrade the Relay-chain's overall performance or place untenable requirements on the storage of full-nodes. To this goal, we require an account sovereign to the Service to be holding an amount of funds proportional to the overall storage footprint of its Child Trie. `set_work_storage` may return an error should the balance requirement not be met. Host functions are provided allowing any state changes to be committed at fail-safe checkpoints to provide resilience in case of weight overrun (or even buggy code which panics). The amount of weight remaining may also be queried without setting a checkpoint. `Weight` is expressed in a regular fashion for a solo-chain (i.e. one-dimensional). -Simple transfers of data and balance between Work Classes are possible by the `transfer` function. This is an entirely synchronous function which transfers the execution over to a `destination` Work Class as well as the provided `amount` into their account. +Simple transfers of data and balance between Servicees are possible by the `transfer` function. This is an entirely synchronous function which transfers the execution over to a `destination` Service as well as the provided `amount` into their account. -A new VM is set up with code according to the Work Class's `accumulate` code blob, but with a secondary entry point whose prototype is: +A new VM is set up with code according to the Service's `accumulate` code blob, but with a secondary entry point whose prototype is: ```rust -fn on_transfer(source: WorkClass, amount: u128, memo: Vec) -> Result, ()>; +fn on_transfer(source: Service, amount: u128, memo: Vec) -> Result, ()>; ``` During this execution, all host functions above may be used except `checkpoint()`. The operation may result in error in which case all changes to state are reverted, including the balance transfer. (Weight is still used.) @@ -459,13 +459,13 @@ Other host functions, including some to access Relay-chain hosted services such _(Note for discussion: Should we be considering light-client proof size at all here?)_ -We can already imagine three kinds of Work Class: *Parachain Validation* (as per Polkadot 1.0), *Actor Progression* (as per Coreplay in a yet-to-be-proposed RFC) and Simple Ordering (placements of elements into a namespaced Merkle trie). Given how abstract the model is, one might reasonably expect many more. +We can already imagine three kinds of Service: *Parachain Validation* (as per Polkadot 1.0), *Actor Progression* (as per Coreplay in a yet-to-be-proposed RFC) and Simple Ordering (placements of elements into a namespaced Merkle trie). Given how abstract the model is, one might reasonably expect many more. ### Work Package Ordering At the point of Reporting of a Work Package (specifically, its Work Report) on-chain, it is trivial to ensure that the ordering respects the optional `prerequisite` field specified in the Work Package, since the RcBA need only avoid Registering any which do not have their prerequisite fulfilled recently. -However, there is a variable delay between a Work Report first being introduced on-chain in the Reporting and its eventual Integration into the Work Class's State due to the asynchronous Availability Protocol. This means that requiring the order at the point of Reporting is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. +However, there is a variable delay between a Work Report first being introduced on-chain in the Reporting and its eventual Integration into the Service's State due to the asynchronous Availability Protocol. This means that requiring the order at the point of Reporting is insufficient for guaranteeing that order at the time of Accumulation. Furthermore, the Availability Protocol may or may not actually complete for any Work Package. Two alternatives present themselves: provide ordering only on a *best-effort* basis, whereby Work Reports respect the ordering requested in their Work Packages as much as possible, but it is not guaranteed. Work Reports may be Accumulated before, or even entirely without, their prerequisites. We refer to this *Soft-Ordering*. The alternative is to provide a guarantee that the Results of Work Packages will always be Accumulated no earlier than the Result of any prerequisite Work Package. As we are unable to alter the Availability Protocol, this is achieved through on-chain queuing and deferred Accumulation. @@ -475,7 +475,7 @@ Both are presented as reasonable approaches for this proposal, though the Soft-O In this alternative, actual ordering is only guaranteed going *into* the Availability Protocol, not at the point of Accumulation. -The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Work Class. The Ordering of Reporting is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. +The (on-chain) repercussion of the Availability Protocol completing for the Work Package is that each Work Result becomes scheduled for Accumulation at the end of the Relay-chain Block Execution along with other Work Results from the same Service. The Ordering of Reporting is replicated here for all Work Results present. If the Availability Protocol delays the Accumulation of a prerequisite Work Result, then the dependent Work Result may be Accumulated in a block prior to that of its dependency. It is assumed that the *Accumulation* logic will be able to handle this gracefully. It is also possible (though unexpected in regular operation) that Work Packages never complete the Availability Protocol. Such Work Packages eventually time-out and are discarded from Relay-chain state. @@ -569,7 +569,7 @@ trait Storage { Internally, data is stored with a reference count so that two separate usages of `store` need not be concerned about the other. -Every piece of data stored for an untrusted caller requires a sizeable deposit. When used by untrusted code via a host function, the `depositor` would be set to an account controlled by the executing code (e.g. the Work Class's sovereign account). +Every piece of data stored for an untrusted caller requires a sizeable deposit. When used by untrusted code via a host function, the `depositor` would be set to an account controlled by the executing code (e.g. the Service's sovereign account). Removing data happens in a two-phase procedure; first the data is unrequested, signalling that calling `lookup` on its hash may no longer work (it may still work if there are other requests active). 24 hours following this, the data is expunged with a second call which, actually removes the data from the chain assuming no other requests for it are active. @@ -582,17 +582,17 @@ Crucially, a *Task* is no longer a first-class concept. Thus the Agile Coretime In this proposal, we replace the concept of a Task with a more general ticketing system; Coretime is assigned to an *Authorizer* instead, a parameterized function. This would allow a succinct *Authorization* (i.e. a small blob of data) to be included in the Work Package which, when fed into the relevant Authorizer function could verify that some Work Package is indeed allowed to utilize that Core at (roughly) that time. A simple proof system would be a regular PKI signature. More complex proof systems could include more exotic cryptography (e.g. multisignatures or zk-SNARKs). -In this model, we would expect any authorized Work Packages which panic or overrun to result in a punishment to the specific author by the logic of the Work Class. +In this model, we would expect any authorized Work Packages which panic or overrun to result in a punishment to the specific author by the logic of the Service. ### Notes for migrating from a Parachain-centric model -All Parachain-specific data held on the Relay-chain including the means of tracking the Head Data and Code would be held in the Parachains Work Class (Child) Trie. The Work Package would be essentially equivalent to the current PoV blob, though prefixed by the Work Class. `refine` would prove the validity of the parachain transition described in the PoV which is the Work Package. The Parachains Work Output would provide the basis for the input of what is currently termed the Paras Inherent. `accumulate` would identify and resolve any colliding transitions and manage message queue heads, much the same as the current hard-coded logic of the Relay-chain. +All Parachain-specific data held on the Relay-chain including the means of tracking the Head Data and Code would be held in the Parachains Service (Child) Trie. The Work Package would be essentially equivalent to the current PoV blob, though prefixed by the Service. `refine` would prove the validity of the parachain transition described in the PoV which is the Work Package. The Parachains Work Output would provide the basis for the input of what is currently termed the Paras Inherent. `accumulate` would identify and resolve any colliding transitions and manage message queue heads, much the same as the current hard-coded logic of the Relay-chain. -We should consider utilizing the Storage Pallet for Parachain Code and store only a hash in the Parachains Work Class Trie. +We should consider utilizing the Storage Pallet for Parachain Code and store only a hash in the Parachains Service Trie. ### Notes for implementing the Actor Progression model -Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Work Class Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Work Class Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. +Actor code is stored in the Storage Pallet. Actor-specific data including code hash, VM memory hash and sequence number is stored in the Actor Service Trie under that Actor's identifier. The Work Package would include pre-transition VM memories of actors to be progressed whose hash matches the VM memory hash stored on-chain and any additional data required for execution by the actors (including, perhaps, swappable memory pages). The `refine` function would initiate the relevant VMs and make entries into those VMs in line with the Work Package's manifest. The Work Output would provide a vector of actor progressions made including their identifer, pre- and post-VM memory hashes and sequence numbers. The `accumulate` function would identify and resolve any conflicting progressions and update the Actor Service Trie with the progressed actors' new states. More detailed information is given in the Coreplay RFC. ### UMP, HRMP and Work Output bounding @@ -600,11 +600,11 @@ At present, both HRMP (the stop-gap measure introduced in lieu of proper XCMP) a The present proposal brings soundness to this situation by limiting the amount of data which can arrive on the Relay-chain from each Work Item, and by extension from each Work Package. The specific limit proposed is 4KB per Work Item, which if we assume an average of two Work Items per Package and 250 cores, comes to a manageable 2MB and leaves plenty of headroom. -However, this does mean that pre-existing usage of UMP and HRMP are impossible. In any case, UMP is removed entirely from the Work Class API. +However, this does mean that pre-existing usage of UMP and HRMP are impossible. In any case, UMP is removed entirely from the Service API. -To make up for this change, all non-"kernel" Relay-chain functionality will exist within Work Classes (parachains under CoreChains, or possibly even actors under CorePlay). This includes staking and governance functionality. The development and deployment of XCMP avoids the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. APIs are provided for the few operations remaining which the Relay-chain must provide (validator updates, code updates and core assignments) but may only be used by Work Classes holding the appropriate privileges. Thus taken together, neither HRMP, UMP or DMP will exist. +To make up for this change, all non-"kernel" Relay-chain functionality will exist within Servicees (parachains under CoreChains, or possibly even actors under CorePlay). This includes staking and governance functionality. The development and deployment of XCMP avoids the need to place any datagrams on the Relay-chain which are not themselves meant for interpretation by it. APIs are provided for the few operations remaining which the Relay-chain must provide (validator updates, code updates and core assignments) but may only be used by Servicees holding the appropriate privileges. Thus taken together, neither HRMP, UMP or DMP will exist. -An initial and hybrid deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Work Class to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. +An initial and hybrid deployment of CoreJam could see the Work Output size limits temporarily increased for the Parachains Service to ensure existing use-cases do not suffer, but with a published schedule on reducing these to the eventual 4KB limits. This would imply the need for graceful handling by the RcBA should the aggregated Work Outputs be too large. ### Notes on Implementation Order @@ -612,21 +612,21 @@ In order to ease the migration process from the current Polkadot on- and off-cha We therefore envision an initial version of this proposal with minimal modifications to current code: -1. Remain with Webassembly rather than RISC-V, both for Work Class logic and the subordinate environments which can be set up from Work Class logic. The introduction of Work Classes is a permissioned action requiring governance intervention. Work Packages will otherwise execute as per the proposal. *Minor changes to the status quo.* +1. Remain with Webassembly rather than RISC-V, both for Service logic and the subordinate environments which can be set up from Service logic. The introduction of Servicees is a permissioned action requiring governance intervention. Work Packages will otherwise execute as per the proposal. *Minor changes to the status quo.* 2. Attested Work Packages must finish running in time and not panic. Therefore `WorkResult` must have an `Infallible` error type. If an Attestation is posted for a Work Package which panics or times out, then this is a slashable offence. *No change to the status quo.* 3. There should be full generalization over Work Package contents, as per the proposal. Introduction of Authorizers, `refine`, `prune` and `accumulate`. *Additional code to the status quo.* -To optimize the present situation, a number of "natively implemented", fixed-function registrations will be provided. The Work Class of index zero will be used to represent the Parachains Work Class and will have a "native" (i.e. within the Wasm runtime) implementation of `refine` and `accumulate`. Secondly, a fixed-function set of Auth IDs 9,999 and lower simply represent Authorizers which accept Work Packages which contain a single Work Item of the Parachains Work Class which pertain to progressing a parachain of ID equal to the Auth ID value. +To optimize the present situation, a number of "natively implemented", fixed-function registrations will be provided. The Service of index zero will be used to represent the Parachains Service and will have a "native" (i.e. within the Wasm runtime) implementation of `refine` and `accumulate`. Secondly, a fixed-function set of Auth IDs 9,999 and lower simply represent Authorizers which accept Work Packages which contain a single Work Item of the Parachains Service which pertain to progressing a parachain of ID equal to the Auth ID value. -Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Work Classes. +Later implementation steps would polish (1) to replace with RISC-V (with backwards compatibility) and polish (2) to support posting receipts of timed-out/failed Work Packages on-chain for RISC-V Servicees. -A final transition may migrate the Parachains Work Class to become a regular permissionless Work Class module. +A final transition may migrate the Parachains Service to become a regular permissionless Service module. ## Performance, Ergonomics and Compatibility -The present proposal is broadly compatible with the facilities of the Legacy Model pending the integration of a Work Class specific to Parachains. Unlike other Work Classes, this is expected to be hard-coded into the Relay-chain runtime to maximize performance, compatibility and implementation speed. +The present proposal is broadly compatible with the facilities of the Legacy Model pending the integration of a Service specific to Parachains. Unlike other Servicees, this is expected to be hard-coded into the Relay-chain runtime to maximize performance, compatibility and implementation speed. -Certain changes to active interfaces will be needed. Firstly, changes will be needed for any software (such as _Cumulus_ and _Smoldot_) relying on particular Relay-chain state trie keys (i.e. storage locations) used to track the code and head-data of parachains, so that they instead query the relevant key within the Parachains Work Class Child Trie. +Certain changes to active interfaces will be needed. Firstly, changes will be needed for any software (such as _Cumulus_ and _Smoldot_) relying on particular Relay-chain state trie keys (i.e. storage locations) used to track the code and head-data of parachains, so that they instead query the relevant key within the Parachains Service Child Trie. Secondly, software which currently provides Proofs-of-Validity to Relay-chain Validators, such as _Cumulus_, would need to be updated to use the new Work Item/Work Package format. @@ -638,15 +638,15 @@ The proposal introduces no new privacy concerns. ## Future Directions and Related Material -We expect to see several Work Classes being built shortly after CorePlay is delivered. +We expect to see several Servicees being built shortly after CorePlay is delivered. ## Drawbacks, Alternatives and Unknowns Important considerations include: -1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the Work Items. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Work Class State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). +1. In the case of composite Work Packages, allowing synchronous (and therefore causal) interactions between the Work Items. If this were to be the case, then some sort of synchronisation sentinel would be needed to ensure that should one subpackage result without the expected effects on its Service State (by virtue of the `accumulate` outcome for that subpackage), that the `accumulate` of any causally entangled subpackages takes appropriate account for this (i.e. by dropping it and not effecting any changes from it). -2. Work Items may need some degree of coordination to be useful by the `accumulate` function of their Work Class. To a large extent this is outside of the scope of this proposal's computation model by design. Through the authorization framework we assert that it is the concern of the Work Class and not of the Relay-chain validators themselves. However we must ensure that certain requirements of the parachains use-case are practically fulfillable in *some* way. Within the legacy parachain model, PoVs: +2. Work Items may need some degree of coordination to be useful by the `accumulate` function of their Service. To a large extent this is outside of the scope of this proposal's computation model by design. Through the authorization framework we assert that it is the concern of the Service and not of the Relay-chain validators themselves. However we must ensure that certain requirements of the parachains use-case are practically fulfillable in *some* way. Within the legacy parachain model, PoVs: 1. shouldn't be replayable; 2. shouldn't require unbounded buffering in `accumulate` if things are submitted out-of-order; 3. should be possible to evaluate for ordering by validators making a best-effort. From cfea18f90c71bc0fef2e782e9bf99b42fd43c996 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 27 Oct 2023 14:58:58 +0100 Subject: [PATCH 44/47] Explanation --- text/0031-corejam.md | 50 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index b678167c0..7ad5cf595 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -54,8 +54,8 @@ In order of importance: Short forms of several common term are used here for brevity: -- *RcVG*: Relay-chain Validator Group -- *RcBA*: Relay-chain Block Author +- *RcVG*: Relay-chain Validator Group aka Backing group, a grouping of Relay-chain validators who act as the initial guarantors over the result of some computation done off-chain. +- *RcBA*: Relay-chain Block Author, the author of some particular block on the Relay-chain. ### From Old to New @@ -63,21 +63,23 @@ The current specification of the Polkadot protocol, and with in the Relay-chain Existing functionality relied upon by parachains will continue to be provided as a special case under a more general and permissionless model which is detailed presently and known as *CoreJam*. Transition of Polkadot to be in line with the present proposal will necessarily imply some minor alterations of formats utilized by Cumulus, Smoldot and other light-client APIs (see the section on Compatibility). However, much of the underlying logic (in particular, consensus, disputes and availability) is retained, though its application is generalised. This proposal will only make note of the expectations regarding the changes, and presumes continuation of all other logic. -As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Service*, *Work Item*, *Work Output*, *Work Result*, *Work Report* and *Work Package Attestation*, *Service Trie*. +As part of this model, we introduce a number of new and interrelated concepts: *Work Package*, *Service*, *Work Item*, *Work Output*, *Work Result*, *Work Report*, *Guarantee* and *Service Trie*. Focussing on continuity and reuse of existing logic, it is unsurprising that many of these concepts already analogues in the Parachains model, albeit ones with a less general definition. While this mapping can be helpful to quickly create an approximate understanding of the new concepts for those already familiar with Polkadot, care must be taken not to inadvertantly make incorrect presumptions over exact details of their relationships, constraints, timing, provisions and APIs. Nonetheless, they are provided here for whatever help they may be. | CoreJam model | Legacy model | Context | | --- | --- | --- | +| *Core Chain* | Relay-chain | Primary block-chain | | *Work Package* | Proof-of-Validity | Untrusted data provided to RcVG | | *Work Item* | Proof-of-Validity | State-transition inputs and witness | | *Work Output* | Candidate | State-transition consequence | -| *Work Report* | Candidate | Target of Attestation | -| *(Work Package) Attestation* | Attestation | Output signed by members of RcVG | +| *Work Report* | Candidate | Target of attestation | +| *(Work Package) Attestation* | Attestation | Output signed in attestation | | *Reporting* | Attestation | Placement of Attestation on-chain | | *Integration* | Inclusion | Irreversible transition of state | | *Builder* | Collator | Creator of data worthy of Attestation | + Additionally, the *Service Trie* has no immediate analogue, but may be considered as the Relay-chain state used to track the code and head data of the parachains. ### Overview @@ -128,11 +130,11 @@ A *Work Package* is an *Authorization* together with a series of *Work Items* an (The number of prerequisites of a Work Package is limited to at most one. However, we cannot trivially control the number of dependents in the same way, nor would we necessarily wish to since it would open up a griefing vector for misbehaving Work Package Builders who interrupt a sequence by introducing their own Work Packages with a prerequisite which is within another's sequence.) -Work Items are a pair of service and payload, where the `service` identifies a pairing of code and state known as a *Service* and `payload` is a block of data which, through the aforementioned code, mutates said state in some presumably useful way. +Work Items are a pair where the first item, `service`, itself identifies a pairing of code and state known as a *Service*; and the second item, `payload`, is a block of data which, through the aforementioned code, mutates said state in some presumably useful way. -A Service has certain similarities to an object in a decentralized object-oriented execution environment (or, indeed, a smart contract), with the main difference being a more exotic computation architecture available to it. Similar to smart contracts, a Service's state is stored on-chain and transitioned only using on-chain logic. Also similar to a smart contract, resources used by a Service are strictly and deterministically constrained (using dynamic metering). Finally, Servicees, like smart contracts, are able to hold funds and call into each other synchronously. +A Service has certain similarities to an object in a decentralized object-oriented execution environment (or, indeed, a smart contract), with the main difference being a more exotic computation architecture available to it. Similar to smart contracts, a Service's state is stored on-chain and transitioned only using on-chain logic. Also similar to a smart contract, resources used by a Service are strictly and deterministically constrained (using dynamic metering). Finally, Services, like smart contracts, are able to hold funds and call into each other synchronously. -However, unlike for smart contracts, the on-chain transition logic of a Service (known as the *Accumulate* function) cannot directly be interacted with by actors external to the consensus environment. Concretely, they cannot be transacted with. Aside from the aforementioned inter-service calling, all input data (and state progression) must come as the result of a Work Item. A Work Item is a blob of data meant for a particular Service and crafted by some source external to consensus. It may be thought of as akin to a transaction. The Work Item is first processed *in-core*, which is to say on one of many secure and isolated virtual decentralized processors, yielding a distillate known as a Work Result. It is this Work Result which is collated together with others of the same service and Accumulated into the Service. +However, unlike for smart contracts, the on-chain transition logic of a Service (known as the *Accumulate* function) cannot directly be interacted with by actors external to the consensus environment. Concretely, they cannot be transacted with. Aside from the aforementioned inter-service calling, all input data (and state progression) must come as the result of a Work Item. A Work Item is a blob of data meant for a particular Service and crafted by some source external to consensus. It may be thought of as akin to a transaction. The Work Item is first processed *in-core*, which is to say on one of many secure and isolated virtual decentralized processors, yielding a distillate known as a *Work Result*. It is this Work Result which is collated together with others of the same service and Accumulated into the Service on-chain. In short, a Service may be considered as a kind of smart contract albeit one whose transaction data is first preprocessed with cheap decentralized compute power. @@ -415,26 +417,42 @@ There is an amount of weight which it is allowed to use before being forcibly te However, the actual amount of weight may be substantially more. Each Work Package is allotted a specific amount of weight for all on-chain activity (`weight_per_package` above) and has a weight liability defined by the weight requirements of all Work Items it contains (`total_weight_requirement` above). Any weight remaining after the liability (i.e. `weight_per_package - total_weight_requirement`) may be apportioned to the Servicees of Items within the Report on a pro-rata basis according to the amount of weight they utilized during `refine`. Any weight unutilized by Classes within one Package may be carried over to the next Package and utilized there. ```rust -fn get_work_storage(key: &[u8]) -> Option>; -fn get_work_storage_len(key: &[u8]) -> Option; +/// Simple 32 bit "result" type. `u32::max_value()` down to `u32::max_value() - 255` is used to indicate an error. `0` up to `u32::max_value() - 256` are used to indicate success and a scalar value. +pub type SimpleResult = u32; +/// Returns `u32::max_value()` in case that `key` does not exist. Otherwise, the size of +/// the storage entry is returned and a minimum of this value and `buffer_len` bytes are +/// written into `buffer` from the storage value of `key`. If `buffer_len` is zero, this +/// operation may be additionally optimized to avoid reading any value data. +fn get_work_storage(key: &[u8], buffer: *mut [u8], buffer_len: u32) -> SimpleResult; fn checkpoint() -> Weight; fn weight_remaining() -> Weight; -fn set_work_storage(key: &[u8], value: &[u8]) -> Result<(), ()>; +/// Returns `u32::max_value()` on failure, `0` on success. +fn set_work_storage(key: &[u8], value: &[u8]) -> SimpleResult; fn remove_work_storage(key: &[u8]); -fn set_validators(validator_keys: &[ValidatorKey]) -> Result<(), ()>; -fn set_code(code: &[u8]) -> Result<(), ()>; +/// Returns `u32::max_value()` on failure, `0` on success. +fn set_validators(validator_keys: &[ValidatorKey]) -> SimpleResult; +/// Returns `u32::max_value()` on failure, `0` on success. +fn set_code(code: &[u8]) -> SimpleResult; +/// Returns `u32::max_value()` on failure, `0` on success. fn assign_core( core: CoreIndex, begin: BlockNumber, assignment: Vec<(CoreAssignment, PartsOf57600)>, end_hint: Option, -) -> Result<(), ()>; +) -> SimpleResult; +/// Returns `u32::max_value()` in case that the operation failed or the transfer returned +/// an error. +/// Otherwise, the size of `Vec` returned by the `on_transfer` entry-point entry is +/// returned and as much of the data from the `Vec` as possible is copied into the provided +/// `buffer`. fn transfer( destination: Service, amount: u128, memo: &[u8], weight: Weight, -) -> Result, ()>; + buffer_len: u32, + buffer: *mut [u8], +) -> SimpleResult; ``` Read-access to the entire Relay-chain state is allowed. No direct write access may be provided since `accumulate` is untrusted code. `set_work_storage` may fail if an insufficient deposit is held under the Service's account. @@ -450,7 +468,7 @@ Simple transfers of data and balance between Servicees are possible by the `tran A new VM is set up with code according to the Service's `accumulate` code blob, but with a secondary entry point whose prototype is: ```rust -fn on_transfer(source: Service, amount: u128, memo: Vec) -> Result, ()>; +fn on_transfer(source: Service, amount: u128, memo: Vec, buffer_len: u32) -> Result, ()>; ``` During this execution, all host functions above may be used except `checkpoint()`. The operation may result in error in which case all changes to state are reverted, including the balance transfer. (Weight is still used.) From 6d27b305e1f3cde093d1742c2a705f4b1e8f69d1 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 27 Oct 2023 15:20:18 +0100 Subject: [PATCH 45/47] Note about lookup --- text/0031-corejam.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 7ad5cf595..660453b13 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -237,8 +237,15 @@ Both `refine` and `is_authorized` are only ever executed in-core. Within this en Several host functions (largely in line with the host functions available to Parachain Validation Function code) are supplied. One addition is: ```rust -/// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. -fn lookup(hash: [u8; 32]) -> Vec; +/// Determine the preimage of `hash` utilizing the Relay-chain Storage pallet. This must +/// always do the same thing for the same `context` regardless of the current state of the +/// chain. This is achieved through the usage (by the host) of the specialized Storage +/// pallet. +/// +/// It returns `u32::max_value()` in the case that the preimage is unavailable. Otherwise +/// it returns the length of the preimage and places the first bytes of the preimage into +/// `buffer`, up to a maximum of `buffer_len`. +fn lookup(hash: [u8; 32], buffer: *mut u8, buffer_len: u32) -> u32; ``` Other host functions will allow for the possibility of executing a WebAssembly payload (for example, a Parachain Validation Function) or instantiating and entering a subordinate RISCV VM (for example for Actor Progressions). From c15ef010e34e5a33de9e62e1744b4a5d5329f451 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 27 Oct 2023 15:25:55 +0100 Subject: [PATCH 46/47] Terminology --- text/0031-corejam.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 163a898d4..5acbf1cfd 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -9,7 +9,7 @@ ## Summary -This is a proposal to fundamentally alter the workload done on the Polkadot Relay-chain, both in terms of that which is done "on-chain", i.e. by all Relay Chain Validators (*Validators*) as well as that which is done "in-core", i.e. distributed among subsets of the Validators (*Validator Groups*). The target is to create a model which closely matches the underlying technical architecture and is both generic and permissionlessly extensible. +This is a proposal to fundamentally alter the workload done on the Polkadot Relay-chain, both in terms of that which is done "on-chain", i.e. by all Relay Chain Validators (*Validators*) as well as that which is done "in-core", i.e. distributed among subsets of the Validators (*Backing Groups*). The target is to create a model which closely matches the underlying technical architecture and is both generic and permissionlessly extensible. In the proposed model, code is stored on-chain with two entry-points. Workloads are collated and processed in-core (and thus parallelized) using one entry-point, whereas the refined outputs of this processing are gathered together and an on-chain state-machine progressed according to the other. @@ -54,7 +54,7 @@ In order of importance: Short forms of several common term are used here for brevity: -- *RcVG*: Relay-chain Validator Group aka Backing group, a grouping of Relay-chain validators who act as the initial guarantors over the result of some computation done off-chain. +- *RcBG*: Relay-chain Backing Group, a grouping of Relay-chain validators who act as the initial guarantors over the result of some computation done off-chain. - *RcBA*: Relay-chain Block Author, the author of some particular block on the Relay-chain. ### From Old to New @@ -70,7 +70,7 @@ Focussing on continuity and reuse of existing logic, it is unsurprising that man | CoreJam model | Legacy model | Context | | --- | --- | --- | | *Core Chain* | Relay-chain | Primary block-chain | -| *Work Package* | Proof-of-Validity | Untrusted data provided to RcVG | +| *Work Package* | Proof-of-Validity | Untrusted data provided to RcBG | | *Work Item* | Proof-of-Validity | State-transition inputs and witness | | *Work Output* | Candidate | State-transition consequence | | *Work Report* | Candidate | Target of attestation | @@ -146,7 +146,7 @@ A Work Package has several stages of consensus computation associated with its p While a Work Package is being built, the *Builder* must have access to the Relay-chain state in order to supply a specific *Context*. The Context dictates a certain *Scope* for the Work Package which is used by the Initial Validation to limit which Relay-chain blocks it may be processed on to a small sequence of a specific fork (which is yet to be built, presumably). We define the Relay-chain height at this point to be `T`. -The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Validator Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. +The first consensus computation to be done is the Work Package having its Authorization checked in-core, hosted by the Relay-chain Backing Group. If it is determined to be authorized, then the same environment hosts the Refinement of the Work Package into a series of Work Results. This concludes the bulk of the computation that the Work Package represents. We would assume that the Relay-chain's height at this point is shortly after the authoring time, `T+r` where `r` could be as low as zero. The second consensus computation happens on-chain at the behest of the Relay-chain Block Author of the time `T+r+i`, where `i` is generally zero or one, the time taken for the Work Results to be transported from within the Core to get to the gateway of being on-chain. The computation done essentially just ensures that the Work Package is still in scope and that the prerequisite it relies upon (if any) has been submitted ahead of it. This is called the on-chain *Reporting* (in the fixed-function parachains model, this is known as "attestation") and initiates the *Availability Protocol* for this Work Package once Relay-chain Validators synchronize to the block. This protocol guarantees that the Work Package will be made available for as long as we allow disputes over its validity to be made. @@ -156,17 +156,17 @@ Finally, at some point later still `T+r+i+a+o`, the Results of the Work Package ### Collect-Refine -The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield *Work Results*. Finally, each Validator Group member attests to a Work Package yielding a series of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Service's) state which happens in the following stages. +The first two stages of the CoreJam process are *Collect* and *Refine*. *Collect* refers to the collection and authorization of Work Packages (collections of items together with an authorization) to utilize a Polkadot Core. *Refine* refers to the performance of computation according to the Work Packages in order to yield *Work Results*. Finally, each Backing Group member attests to a Work Package yielding a series of Work Results and these Attestations form the basis for bringing the Results on-chain and integrating them into the Polkadot (and in particular the Service's) state which happens in the following stages. #### Collection and `is_authorized` -Collection is the means of a Validator Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. +Collection is the means of a Backing Group member attaining a Work Package which is authorized to be performed on their assigned Core at the current time. Authorization is a prerequisite for a Work Package to be included on-chain. Computation of Work Packages which are not Authorized is not rewarded. Incorrectly attesting that a Work Package is authorized is a disputable offence and can result in substantial punishment. On arrival of a Work Package, after the initial decoding, a first check is that the `context` field is valid. This must reference a header hash of a known block which may yet be finalized and the additional fields must correspond to the data of that block. Agile Coretime (see [RFC#0001](https://github.com/polkadot-fellows/RFCs/blob/main/text/0001-agile-coretime.md)) prescribes two forms of Coretime sales: Instantaneous and Bulk. Sales of Instantaneous Coretime are no longer provided, leaving only Bulk Coretime. -We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcVGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Service. +We introduce the concept of an *Authorizer* procedure, which is a piece of logic stored on-chain to which Bulk Coretime may be assigned. Assigning some Bulk Coretime to an Authorizer implies allowing any Work Package which passes that authorization process to utilize that Bulk Coretime in order to be submitted on-chain. It controls the circumstances under which RcBGs may be rewarded for evaluation and submission of Work Packages (and thus what Work Packages become valid to submit onto Polkadot). Authorization logic is entirely arbitrary and need not be restricted to authorizing a single collator, Work Package builder, parachain or even a single Service. An *Authorizer* is a parameterized procedure: @@ -186,7 +186,7 @@ The `code_hash` of the Authorizer is assumed to be the hash of some code accessi fn is_authorized(param: &AuthParam, package: &WorkPackage, core_index: CoreIndex) -> bool; ``` -If the `is_authorized` function overruns the system-wide limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with the package's `context` field in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent Relay-chain state. +This function is executed in a metered VM and subject to a modest system-wide limitation on execution time. If it overruns this limit or panicks on some input, it is considered equivalent to returning `false`. While it is mostly stateless (e.g. isolated from any Relay-chain state) it is provided with the package's `context` field in order to give information about a recent Relay-chain block. This allows it to be provided with a concise proof over some recent Relay-chain state. A single `Authorizer` value is associated with the index of the Core at a particular Relay-chain block and limits in some way what Work Packages may be legally processed by that Core. @@ -297,13 +297,13 @@ struct Attestation { report: WorkReport, /// Which validators from the group have a signature in `attestations`. validators: BitVec, - /// The signatures of the RcVG members set out in `validators` whose message is the + /// The signatures of the RcBG members set out in `validators` whose message is the /// hash of the `report`. The order of the signatures is the same order as the validators appear in `validators`. attestations: Vec, } ``` -Each Relay-chain block, every Validator Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to introduce it for Reporting), then that Validator Group is not rewarded for that block. +Each Relay-chain block, every Backing Group representing a Core which is assigned work provides a series of Work Results coherent with an authorized Work Package. Validators are rewarded when they take part in their Group and process such a Work Package. Thus, together with some information concerning their execution context, they sign a *Report* concerning the work done and the results of it. This is also known as a *Candidate*. This signed Report is called an *Attestation*, and is provided to the Relay-chain block author. If no such Attestation is provided (or if the Relay-chain block author refuses to introduce it for Reporting), then that Backing Group is not rewarded for that block. The process continues once the Attestations arrive at the Relay-chain Block Author. @@ -327,7 +327,7 @@ Once both Availability and any additional requirements are met (including orderi There are a number of Initial Validation requirements which the RcBA must do in order to ensure no time is wasted on further, possibly costly, computation. Since the same tests are done on-chain, then for a Block Author to expect to make a valid block these tests must be done prior to actually placing the Attestations in the Relay-chain Block Body. -Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Reporting on-chain. Only one Work Report may be considered for Reporting from each RcVG per block. +Firstly, any given Work Report must have enough signatures in the Attestation to be considered for Reporting on-chain. Only one Work Report may be considered for Reporting from each RcBG per block. Secondly, any Work Reports introduced by the RcBA must be *Recent*, defined as having a `context.header_hash` which is an ancestor of the RcBA head and whose height is less than `RECENT_BLOCKS` from the block which the RcBA is now authoring. @@ -352,7 +352,7 @@ type RecentReports = StorageValue> The RcBA must keep an up to date set of which Work Packages have already been Reported in order to avoid accidentally attempting to introduce a duplicate Work Package or one whose prerequisite has not been fulfilled. Since the currently authored block is considered *Recent*, Work Reports introduced earlier in the same block do satisfy the prerequisite of Work Packages introduced later. -While it will generally be the case that RcVGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RcVGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: +While it will generally be the case that RcBGs know precisely which Work Reports will have been introduced at the point that their Attestation arrives with the RcBA by keeping the head of the Relay-chain in sync, it will not always be possible. Therefore, RcBGs will never be punished for providing an Attestation which fails any of these tests; the Attestation will simply be kept until either: 1. it stops being *Recent*; 2. it becomes Reported on-chain; or From 9e1b05e2ad02145815fae4df530598b8b6e55dab Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 11 Dec 2023 16:01:50 +0100 Subject: [PATCH 47/47] Update text/0031-corejam.md Co-authored-by: ordian --- text/0031-corejam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0031-corejam.md b/text/0031-corejam.md index 5acbf1cfd..b30c5d4c0 100644 --- a/text/0031-corejam.md +++ b/text/0031-corejam.md @@ -25,7 +25,7 @@ More recently, the idea of having small to medium size programs executing withou Beyond delivering additional value through the increased potential for use-cases that this flexibility allows, our motivation extends to gaining stability: a future-proof platform allowing teams to build on it without fear of high maintenance burden, continuous bitrot or a technological rug-pull at some later date. Secondly, we are motivated by reducing barriers for new teams, allowing the Polkadot platform to harness the power of the crowd which permissionless systems uniquely enable. -Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the serviceical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. +Being extensible, the Relay-chain becomes far more open to experimentation within this paradigm than the classical Parachain Proof-of-Validity and Validation Function as is the case at present. Being permissionless opens Polkadot experimentation to individuals and teams beyond those core developers. ## Requirements