From c94858e099a19839a135a8b5c41ad38470d430ee Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 29 Jun 2023 17:52:37 +0200 Subject: [PATCH 01/44] RFC-0001 --- RFC-0001-Agile Coretime.md | 270 +++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 RFC-0001-Agile Coretime.md diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md new file mode 100644 index 0000000..f4c74ad --- /dev/null +++ b/RFC-0001-Agile Coretime.md @@ -0,0 +1,270 @@ +# RFC-1: Agile Coretime + +| Status | Proposal | +| --------------- | ------------------------------------------------------------------ | +| **Areas** | General | +| **Description** | Agile periodic-sale-based model for assigning Coretime on the Polkadot Ubiquitous Computer. | +| **Issues** | None | +| **Authors** | Gavin Wood | +| **Reviewers** | None | + + +## Summary + +This proposes a periodic, sale-based method for assigning and Polkadot Coretime. The method takes into account the need for long-term capital expenditure planning for teams building on Polkadot, yet also provides a means to allow Polkadot to capture long-term value in the resource which it sells. It supports the possibility of building secondary markets to make resource allocation more efficient and largely avoids the need for parameterisation. + +## Motivation + +### Present System + +The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for 6six months at a time up to 18 months in advance. Practically speaking, we only see two year periods being bid upon and leased. + +Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expirt of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. + +### Problems + +The present system is based on a model of one-core-per-parachain. This is a legacy interpretation of the Polkadot platform and is not a reflection of its present capabilities. By restricting ownership and usage to this model, more dynamic and resource-efficient means of utilising the Polkadot Ubiquitous Computer is lost. + +More specifically, it is impossible to lease out cores at anything less than six months, and apparently unrealistic to do so at anything less than two years. This cuts out the ability to dynamically manage the underlying resource, and generally experimentation, iteratation and innovation are hampered. It bakes into the platform an assumption of permanence for anything deployed into it and restricts the market's ability to find a more optimal allocation of the finite resource. + +There is no ability to determine capital requirements for hosting a parachain beyond two years from the point of its initial deployment onto Polkadot. While it would be unreasonable to have perfect and indefinite cost predictions for any real-world platform, not having any clarity whatsoever beyond "market rates" two years hence can be a very off-putting prospect for teams to buy into. + +However, quite possibly the most substantial problem is both a perceived and often real high barrier to entry of the Polkadot ecosystem. By forcing innovators to either raise 7-figure sums through investors or appeal to the wider token-holding community, Polkadot makes it essentially difficult for a small band of innovators from deploying their technology into Polkadot. While not being actually permissioned, it is also far from the barrierless, permissionless ideal which an innovation platform such as Polkadot should be striving for. + +## Stakeholders + +Primary stakeholder sets are: + +- Protocol researchers and developers, largely represented by the Polkadot Fellowship and Parity Technologies' Engineering division. +- Polkadot Parachain teams both present and future, and their users. +- Polkadot DOT token holders. + +_Facilitator:_ + +Parity Technologies' Ecosystem division. + +_Reviewers:_ + +None. + +_Consulted:_ +- Alistair +- Jonas +- Bjorn +- Rob H +- Rob K + +_Socialization:_ + +This RFC was presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A small amount of socialisation at the Parachain Summit preceeded it and some substantial discussion followed it. Parity Ecosystem team is currently soliciting views from ecosystem teams who would be key stakeholders. + +## Requirements + +There are five main requirements: + +1. The solution MUST provide an acceptable value-capture mechanism for the Polkadot network. +2. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions. +3. The solution SHOULD minimize the barriers to entry in the ecosystem. +4. The solution SHOULD maximize the value which the Polkadot UC provides by allocating its limited resources optimally. +5. The design MUST work with a limited set of resources (cores on the Polkadot UC) whose properties and number may evolve over time. +6. The design MUST avoid creating additional dependency on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. This includes any dependency on the Relay-chain hosting a DOT token. + +Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC would seem reasonable. + +## Parameters + +This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, either *suggested* in which case it is non-binding and the proposal should not be judged on the value since the governance mechanism of Polkadot is expected to specify/maintain it; or *specified* in which case the proposal should be judged on the merit of the value as-is. + +| Name | Value | +| ------------------- | ---------------------------- | +| `BULK_PERIOD` | `28 * DAYS` | +| `TIMESLICE` | `10 * MINUTES` | +| `BULK_TARGET` | `30` | +| `BULK_LIMIT` | `45` | +| `LEADIN_PERIOD` | `14 * DAYS` | +| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | + + +## Design + +### Overview + +Upon implementation of this proposal, slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk* and *Instantaneous*. This proposal only mandates the implementation of *Bulk Coretime*; any mentions of *Instantaneous Coretime* should be treated only in terms of recommendation. + +*Bulk Coretime* is sold periodically and allocated any time in advance of its usage, whereas *Instantaneous Coretime* is sold immediately prior to usage on a block-by-block basis with an explicit allocation at the time of purchase. + +All Bulk Coretime sold by Polkadot is done so on a new system parachain known as the *Broker-chain*. Owners of Bulk Coretime are tracked on this chain and the ownership status and properties (i.e. the specific period) of the owned Coretime are exposed over XCM as a non-fungible asset. + +At the request of the owner, the Broker-chain allows Bulk Coretime to be: + +1. Transferred in ownership. +2. Split into quantized, non-overlapping segments of Bulk Coretime with the same ownership. +3. Consumed in exchange for the allocation of a Core during its period. +4. Consumed in exchange for a pro-rata amount of the revenue from Instantaneous Core sales over its period. + +Pre-existing leases SHALL be recorded in the Broker-chain and cores reserved for them. + +Sales of Instantaneous Coretime is expected to happen on the Polkadot Relay-chain. The Relay-chain is expected to be responsible for: + +- holding non-transferable, non-refundable DOT balance information for collators. +- setting and adjusting the price of Instantaneous Coretime based on usage. +- allowing collators to consume their DOT balance in exchange for the ability to schedule one PoV for near-immediate usage. +- ensuring the Broker-chain has timely book-keeping information on Coretime sales revenue. This should include a total instantaneous revenue amount for each block number. + +### Broker-chain + +The Broker-chain is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with scheduling information of: + +- How many cores which should be made available during the next session. +- Which cores should be allocated to which para IDs. + +Any cores left unallocated are assumed by the Broker-chain to be used for Instantaneous Coretime sales. + +It is also expected to receive information from the Relay-chain on total revenue from instantaneous coretime sales on a per-block basis. + +### Bulk Sales + +There is a periodic sale of Coretime happening at a period of `BULK_PERIOD`. A number of instances of Coretime spanning from `LEADIN_PERIOD` in the future to `LEADIN_PERIOD + BULK_PERIOD` in the future of a single Polkadot UC Core is offered by the Polkadot System at a fixed price. + +These instances which are owned by purchaser are called *regions*. This sale happens on the Broker-chain. Regions are quantized into atomic periods of `TIMESLICE`, into which `BULK_PERIOD` divides a whole number of times. The `Timeslice` type is a `u16` which can be multiplied by `TIMESLICE` to give a `BlockNumber` value indicating the same period in times of (Relay-chain) blocks. + +The Broker-chain aims to sell some `BULK_TARGET` of Cores, up to some `BULK_LIMIT`. It makes this sale in a single batch `LEADIN_PERIOD` prior to the beginning of the period being sold. The Broker chain publishes a price for this batch of sales for the `BULK_TARGET` period prior to the sale execution. + +Accounts may call a `purchase` function with the appropriate funds in place to have their funds reserved and signal an intention to purchase Bulk Coretime for the forthcoming period. One account may have only one pending purchase. Any number of accounts may attempt to `purchase` Bulk Coretime for the forthcoming period, but the order is recorded. + +If there are more purchases than available cores for purchase in this period, then any additional purchase orders are carried over but marked as such. A purchase is only cancellable if it was carried over. + +When a block of Bulk Coretime is initially issued through this purchase, the price it was purchased for is recorded, in addition to the beginning and end Relay-chain block numbers to which it corresponds. + +The Broker-chain SHALL record this information in a storage double-map called Regions, keyed first by the current bulk sale index (a `u32` starting at zero and incrementing with each sale), then secondarily by a `RegionId`. It shall map into a value of `RegionRecord`: + +```rust +struct RegionId { + core: CoreIndex, // A `u16`. + begin: Timeslice, // A `u16`. +} +struct RegionRecord { + owner: AccountId, + end: Timeslice, + price: Option, + allocation: Option>, // begins set to `None` +} +``` + +Notably, if a region is split or transferred, then the `price` is reset to `None`. + +The Broker-chain provides feedback to the Relay-chain on which `ParaId` sets should be serviced on which cores, and does so as they change. Each block, the Broker-chain checks if any timeslices have elapsed and if so checks to see if any cores have a newly active `RegionRecord` value in the `Regions` map. If they do it MUST notify the Relay-chain of the new responsibilities of the relevant `core`. In this case, it MUST remove the item from the `Regions` map. + +If the `RegionRecord` value for an elapsed `RegionId` has an `allocation` of `None`, then the item is not removed and the Relay-chain is instructed to place the core for instantaneous use. + +### Specific functions of the Broker-chain + +Several functions of the Broker-chain SHALL be exposed through dispatchables and/or a `nonfungible` trait implementation integrated into XCM: + +#### 1. Transfer of ownership + +A `transfer(region: RegionId, new_owner: AccountId)` dispatchable SHALL have the effect of altering the current `owner` field of `region` in the `Regions` map from the signed origin to `new_owner`. + +An implementation of the `nonfungible` trait SHOULD include equivalent functionality. `RegionIndex` SHOULD be used for the `AssetInstance` value. + +In both cases, the `price` field SHALL become `None`. + +#### 2. Split into segments + +A `split(region: RegionId, pivot: Timeslice)` dispatchable SHALL have the effect of mutating the Regions entry of key `region` so that the `end` field becomes `pivot` and create a new Regions entry of the same `core` but a `begin` equal to `pivot` whose `RegionRecord` has the same `owner`, `end` and `allocation` fields as the origin value of `region`. + +`price` in both records is/becomes `None`. + +Also: +- `owner` field of `region` must the equal to the Signed origin. +- `pivot` must equal neither the `begin` nor `end` fields of the `region`. + +#### 3. Consumed in exchange for allocation + +A dispatchable `allocate(region: RegionId, target: Vec)` SHALL be provided corresponding to the `allocate` function described above. + +It MUST be called with a Signed origin equal to the `owner` field of the value of the Regions map for the `region` key. The `allocation` field of this value MUST be `None` (a region may not be re-allocated). + +On success, the `allocation` value is changed to `Some` whose inner value is equal to `target`. + +If the `begin` field of the `region` parameter is less than the current `Timeslice` value, then it is removed and re-entered with the current `Timeslice` value plus one, unless this would be equal to or greater than the `end` field of the corresponding `RegionRecord` value. + +Initially `target` values with only one item MAY be supported. + +If the `RegionRecord`'s `price` field is `Some` (indicating that the Region is freshly purchased), then the Broker-chain SHALL record the `Vec` and `price` together with the `BlockNumber` of the forthcoming Bulk sales period in a map called AllowedRenewals. + +#### 4. Renewals + +A dispatchable `renew(target: Vec)` SHALL be provided. Any account may call `renew` together with a `Vec` to purchase a core and renew an active allocation. + +This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` containing the same `Vec`, except that: + +1. The purchase always succeeds and as such must be processed prior to regular `purchase` orders. +2. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP` of itself or the regular price, whichever is lower. + +AllowedRenewals map is updated with the new information ready for the following Bulk sale. + +### Notes on Instantaneous Core Sales + +For access to the Instantaneous Core sales we may include an `allocate_instantaneous` function. This should allocate the Coretime for usage by Polkadot to serve instantaneous requests and allow the `owner` to collect a pro-rata amount of total Instantaneous sales revenue. + +For an efficient market to form around the provision of Bulk-purchased Cores into the pool of cores available for Instantaneous Coretime purchase, it is crucial to ensure that price changes for the purchase of Instantaneous Coretime are reflected well in the revenues of private Coretime providers during the same period. + +In order to ensure this, then it is crucial that Instantaneous Coretime, once purchased, cannot be held indefinitely prior to eventual use since, if this were the case, a nefarious collator could purchase Coretime when cheap and utilize it some time later when expensive and deprive private Coretime providers of their revenue. It SHOULD therefore be assumed that Instantaneous Coretime, once purchased, has a definite and short "shelf-life", after which it becomes unusable. This incentivizes collators to avoid purchasing Coretime unless they expect to utilize it imminently and thus helps create an efficient market-feedback mechanism whereby a higher price will actually result in material revenues for private Coretime providers who contribute to the pool of Cores available to service Instantaneous Coretime purchases. + +## Implementation + +Implementation of this proposal comes in three phases: + +1. Finalise the specifics of implementation; this may be done through a design document or through a well-documented prototype implementation. +2. Implement the design, including all associated aspects such as unit tests, benchmarks and any support software needed. +3. If any new parachain is required, launch of this. +4. Formal audit of the implementation and any manual testing. +5. Announcement to the various stakeholders of the imminent changes. +6. Software integration and release. +7. Governance upgrade proposal(s). +8. Monitoring of the upgrade process. + +## Performance + +This proposal has no immediate performance considerations. + +## Ergonomics + +This proposal makes no changes to the existing end-user experience. + +## Backwards Compatibility + +Parachains already deployed into the Polkadot UC MUST have a clear plan of action to migrate to an agile Coretime market. + +## Security considerations + +A regular security review SHOULD be conducted prior to deployment through a review by the Web3 Foundation economic research group. + +Any final implementation MUST pass a professional external security audit. + +## Privacy considerations + +The proposal introduces no new privacy concerns. + +## Testing + +Regular testing through unit tests, integration tests, manual testnet tests, zombie-net tests and fuzzing SHOULD be conducted. + +## Documentation + +While this proposal does not introduce documentable features per se, adequate documentation must be provided to potential purchasers of Polkadot Coretime. This SHOULD include any alterations to the Polkadot-SDK software collection, most likely Cumulus. + +## Drawbacks, alternatives, and unknowns + +What are the costs of implementing this proposal? + +What other strategies might solve the same problem? + +What questions still need to be resolved, or details iterated upon, to accept +this proposal? Your answer to this is likely to evolve as the proposal evolves. + +## Prior art and references + +None. + From 72d2787adfd46d75de3b2b406e3e5906f8276804 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 30 Jun 2023 11:00:34 +0200 Subject: [PATCH 02/44] Typo --- RFC-0001-Agile Coretime.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index f4c74ad..2303b62 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -1,12 +1,13 @@ # RFC-1: Agile Coretime -| Status | Proposal | -| --------------- | ------------------------------------------------------------------ | -| **Areas** | General | +| | | +| --------------- | ------------------------------------------------------------------------------------------- | +| **Status** | Draft Proposal | +| **Areas** | General | | **Description** | Agile periodic-sale-based model for assigning Coretime on the Polkadot Ubiquitous Computer. | -| **Issues** | None | -| **Authors** | Gavin Wood | -| **Reviewers** | None | +| **Issues** | n/a | +| **Authors** | Gavin Wood | +| **Reviewers** | None | ## Summary @@ -214,7 +215,7 @@ In order to ensure this, then it is crucial that Instantaneous Coretime, once pu ## Implementation -Implementation of this proposal comes in three phases: +Implementation of this proposal comes in several phases: 1. Finalise the specifics of implementation; this may be done through a design document or through a well-documented prototype implementation. 2. Implement the design, including all associated aspects such as unit tests, benchmarks and any support software needed. @@ -257,14 +258,8 @@ While this proposal does not introduce documentable features per se, adequate do ## Drawbacks, alternatives, and unknowns -What are the costs of implementing this proposal? - -What other strategies might solve the same problem? - -What questions still need to be resolved, or details iterated upon, to accept -this proposal? Your answer to this is likely to evolve as the proposal evolves. +None at present. ## Prior art and references None. - From 5ec35d1a92b63ef7d808cbf60abd203be0c4e43e Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Fri, 30 Jun 2023 11:06:18 +0200 Subject: [PATCH 03/44] Typo --- RFC-0001-Agile Coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 2303b62..d7a3480 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -61,7 +61,7 @@ This RFC was presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A ## Requirements -There are five main requirements: +There are six main requirements: 1. The solution MUST provide an acceptable value-capture mechanism for the Polkadot network. 2. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions. From ef71a4b45f407ee022024471f63c249aab90c44e Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 30 Jun 2023 15:27:29 +0200 Subject: [PATCH 04/44] Detail on migrations --- RFC-0001-Agile Coretime.md | 77 ++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index f4c74ad..886b5e3 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -1,12 +1,13 @@ # RFC-1: Agile Coretime -| Status | Proposal | -| --------------- | ------------------------------------------------------------------ | -| **Areas** | General | +| | | +| --------------- | ------------------------------------------------------------------------------------------- | +| **Status** | Draft Proposal | +| **Areas** | General | | **Description** | Agile periodic-sale-based model for assigning Coretime on the Polkadot Ubiquitous Computer. | -| **Issues** | None | -| **Authors** | Gavin Wood | -| **Reviewers** | None | +| **Issues** | n/a | +| **Authors** | Gavin Wood | +| **Reviewers** | None | ## Summary @@ -17,7 +18,7 @@ This proposes a periodic, sale-based method for assigning and Polkadot Coretime. ### Present System -The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for 6six months at a time up to 18 months in advance. Practically speaking, we only see two year periods being bid upon and leased. +The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 18 months in advance. Practically speaking, we only see two year periods being bid upon and leased. Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expirt of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. @@ -60,7 +61,7 @@ This RFC was presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A ## Requirements -There are five main requirements: +There are six main requirements: 1. The solution MUST provide an acceptable value-capture mechanism for the Polkadot network. 2. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions. @@ -99,7 +100,7 @@ At the request of the owner, the Broker-chain allows Bulk Coretime to be: 1. Transferred in ownership. 2. Split into quantized, non-overlapping segments of Bulk Coretime with the same ownership. -3. Consumed in exchange for the allocation of a Core during its period. +3. Consumed in exchange for the allocation of a Core during its period. 4. Consumed in exchange for a pro-rata amount of the revenue from Instantaneous Core sales over its period. Pre-existing leases SHALL be recorded in the Broker-chain and cores reserved for them. @@ -132,16 +133,17 @@ The Broker-chain aims to sell some `BULK_TARGET` of Cores, up to some `BULK_LIMI Accounts may call a `purchase` function with the appropriate funds in place to have their funds reserved and signal an intention to purchase Bulk Coretime for the forthcoming period. One account may have only one pending purchase. Any number of accounts may attempt to `purchase` Bulk Coretime for the forthcoming period, but the order is recorded. -If there are more purchases than available cores for purchase in this period, then any additional purchase orders are carried over but marked as such. A purchase is only cancellable if it was carried over. +If there are more purchase requests than available cores for purchase in this period, then requests are processed in incoming order. Any additional purchase orders are carried over but marked as such. A purchase is only cancellable if it was carried over. -When a block of Bulk Coretime is initially issued through this purchase, the price it was purchased for is recorded, in addition to the beginning and end Relay-chain block numbers to which it corresponds. +When a region of Bulk Coretime is initially issued through this purchase, the price it was purchased for is recorded, in addition to the beginning and end Relay-chain block numbers to which it corresponds. -The Broker-chain SHALL record this information in a storage double-map called Regions, keyed first by the current bulk sale index (a `u32` starting at zero and incrementing with each sale), then secondarily by a `RegionId`. It shall map into a value of `RegionRecord`: +The Broker-chain SHALL record this information in a storage double-map called Regions, keyed first by the current bulk `SaleIndex` (a `u16` starting at zero and incrementing with each sale), then secondarily by a `RegionId`. It shall map into a value of `RegionRecord`: ```rust +type SaleIndex = u16; struct RegionId { - core: CoreIndex, // A `u16`. - begin: Timeslice, // A `u16`. + core: CoreIndex, // A `u16`. + begin: Timeslice, // A `u16`. } struct RegionRecord { owner: AccountId, @@ -151,9 +153,13 @@ struct RegionRecord { } ``` -Notably, if a region is split or transferred, then the `price` is reset to `None`. +This map functions essentially as a linked list. With one region's `end` field functioning as the next region's key's `begin` field. It is keyyed by the sale index in order to allow the following sale period's Coretime to be manipulated during the `LEADIN_PERIOD` prior to it becoming allocatable. -The Broker-chain provides feedback to the Relay-chain on which `ParaId` sets should be serviced on which cores, and does so as they change. Each block, the Broker-chain checks if any timeslices have elapsed and if so checks to see if any cores have a newly active `RegionRecord` value in the `Regions` map. If they do it MUST notify the Relay-chain of the new responsibilities of the relevant `core`. In this case, it MUST remove the item from the `Regions` map. +An additional storage map is maintained to keep the "heads" of this linked list. It is called `NextRegion` and it maps `CoreIndex` to `Timeslice`, to indicate the earliest stored region of the given core. + +Notably, if a region is split or transferred, then the `price` is reset to `None`, which has the effect of disallowing a price-increase-capped renewal. + +The Broker-chain provides feedback to the Relay-chain on which `ParaId` sets should be serviced on which cores, and does so as they change. Each block, the Broker-chain checks if the period of a `Timeslice` has elapsed. If so, it checks to see if any cores have a newly active `RegionRecord` value in the `Regions` map. If they do, it MUST notify the Relay-chain of the new responsibilities of the relevant `core`. In this case, it MUST remove the item from the `Regions` map and update the `NextRegion` map so it maps the relevant core to the value of removed record's `end` field. If the `RegionRecord` value for an elapsed `RegionId` has an `allocation` of `None`, then the item is not removed and the Relay-chain is instructed to place the core for instantaneous use. @@ -165,7 +171,7 @@ Several functions of the Broker-chain SHALL be exposed through dispatchables and A `transfer(region: RegionId, new_owner: AccountId)` dispatchable SHALL have the effect of altering the current `owner` field of `region` in the `Regions` map from the signed origin to `new_owner`. -An implementation of the `nonfungible` trait SHOULD include equivalent functionality. `RegionIndex` SHOULD be used for the `AssetInstance` value. +An implementation of the `nonfungible` trait SHOULD include equivalent functionality. `RegionId` SHOULD be used for the `AssetInstance` value. In both cases, the `price` field SHALL become `None`. @@ -191,11 +197,19 @@ If the `begin` field of the `region` parameter is less than the current `Timesli Initially `target` values with only one item MAY be supported. -If the `RegionRecord`'s `price` field is `Some` (indicating that the Region is freshly purchased), then the Broker-chain SHALL record the `Vec` and `price` together with the `BlockNumber` of the forthcoming Bulk sales period in a map called AllowedRenewals. +If the `RegionRecord`'s `price` field is `Some` (indicating that the Region is freshly purchased), then the Broker-chain SHALL record an entry in a storage map called AllowedRenewals. This maps a `CoreIndex` to a struct `RenewalRecord`: + +```rust +struct RenewalRecord { + target: Vec, + price: Balance, + sale: SaleIndex, +} +``` #### 4. Renewals -A dispatchable `renew(target: Vec)` SHALL be provided. Any account may call `renew` together with a `Vec` to purchase a core and renew an active allocation. +A dispatchable `renew(core: CoreIndex)` SHALL be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` containing the same `Vec`, except that: @@ -204,6 +218,19 @@ This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly lik AllowedRenewals map is updated with the new information ready for the following Bulk sale. +#### 5. Migrations from Legacy Slot Leases + +It is intended that paras which hold a lease from the legacy slot auction system are able to seamlessly transition into the Agile Coretime framework. + +A dispatchable `migrate(core: CoreIndex)` SHALL be provided. Any account may call `migrate` to purchase Bulk Coretime at the current price for the given `core`. + +This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` with a value of `Vec` containing just one item equal to `target`, except that: + +1. The purchase always succeeds and as such MUST be processed prior to regular `purchase` orders. +2. There MUST be an ongoing legacy parachain lease whose parachain is assigned to `core` and which is due to expire during the Coretime period being purchased. + +AllowedRenewals map is updated with this information ready for the following Bulk sale. + ### Notes on Instantaneous Core Sales For access to the Instantaneous Core sales we may include an `allocate_instantaneous` function. This should allocate the Coretime for usage by Polkadot to serve instantaneous requests and allow the `owner` to collect a pro-rata amount of total Instantaneous sales revenue. @@ -214,7 +241,7 @@ In order to ensure this, then it is crucial that Instantaneous Coretime, once pu ## Implementation -Implementation of this proposal comes in three phases: +Implementation of this proposal comes in several phases: 1. Finalise the specifics of implementation; this may be done through a design document or through a well-documented prototype implementation. 2. Implement the design, including all associated aspects such as unit tests, benchmarks and any support software needed. @@ -257,14 +284,8 @@ While this proposal does not introduce documentable features per se, adequate do ## Drawbacks, alternatives, and unknowns -What are the costs of implementing this proposal? - -What other strategies might solve the same problem? - -What questions still need to be resolved, or details iterated upon, to accept -this proposal? Your answer to this is likely to evolve as the proposal evolves. +None. ## Prior art and references -None. - +None. \ No newline at end of file From f1c3ce416946d09457c662aedb53bc4e9be0891b Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 4 Jul 2023 15:10:34 +0200 Subject: [PATCH 05/44] Update RFC-0001-Agile Coretime.md Co-authored-by: Keith Yeung --- RFC-0001-Agile Coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 110ee06..0df32e1 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -26,7 +26,7 @@ Funds behind the bids made in the slot auctions are merely locked, not consumed The present system is based on a model of one-core-per-parachain. This is a legacy interpretation of the Polkadot platform and is not a reflection of its present capabilities. By restricting ownership and usage to this model, more dynamic and resource-efficient means of utilising the Polkadot Ubiquitous Computer is lost. -More specifically, it is impossible to lease out cores at anything less than six months, and apparently unrealistic to do so at anything less than two years. This cuts out the ability to dynamically manage the underlying resource, and generally experimentation, iteratation and innovation are hampered. It bakes into the platform an assumption of permanence for anything deployed into it and restricts the market's ability to find a more optimal allocation of the finite resource. +More specifically, it is impossible to lease out cores at anything less than six months, and apparently unrealistic to do so at anything less than two years. This cuts out the ability to dynamically manage the underlying resource, and generally experimentation, iteration and innovation are hampered. It bakes into the platform an assumption of permanence for anything deployed into it and restricts the market's ability to find a more optimal allocation of the finite resource. There is no ability to determine capital requirements for hosting a parachain beyond two years from the point of its initial deployment onto Polkadot. While it would be unreasonable to have perfect and indefinite cost predictions for any real-world platform, not having any clarity whatsoever beyond "market rates" two years hence can be a very off-putting prospect for teams to buy into. From f84d0307573e86f02cb1f91030dd7def2cde62aa Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 4 Jul 2023 15:16:26 +0200 Subject: [PATCH 06/44] Update RFC-0001-Agile Coretime.md Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- RFC-0001-Agile Coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 0df32e1..2a5e471 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -12,7 +12,7 @@ ## Summary -This proposes a periodic, sale-based method for assigning and Polkadot Coretime. The method takes into account the need for long-term capital expenditure planning for teams building on Polkadot, yet also provides a means to allow Polkadot to capture long-term value in the resource which it sells. It supports the possibility of building secondary markets to make resource allocation more efficient and largely avoids the need for parameterisation. +This proposes a periodic, sale-based method for assigning Polkadot Coretime. The method takes into account the need for long-term capital expenditure planning for teams building on Polkadot, yet also provides a means to allow Polkadot to capture long-term value in the resource which it sells. It supports the possibility of building secondary markets to make resource allocation more efficient and largely avoids the need for parameterisation. ## Motivation From e020237966a5127ed407a58537fc4a5222b0a8de Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 4 Jul 2023 15:16:45 +0200 Subject: [PATCH 07/44] Update RFC-0001-Agile Coretime.md Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- RFC-0001-Agile Coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 2a5e471..140defc 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -20,7 +20,7 @@ This proposes a periodic, sale-based method for assigning Polkadot Coretime. The The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 18 months in advance. Practically speaking, we only see two year periods being bid upon and leased. -Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expirt of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. +Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expiry of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. ### Problems From 2bfc3def27ab3c3c8820b6bd6457dc6541ddd145 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 4 Jul 2023 16:41:59 +0200 Subject: [PATCH 08/44] Update RFC-0001-Agile Coretime.md Co-authored-by: asynchronous rob --- RFC-0001-Agile Coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 140defc..ff4ba1a 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -153,7 +153,7 @@ struct RegionRecord { } ``` -This map functions essentially as a linked list. With one region's `end` field functioning as the next region's key's `begin` field. It is keyyed by the sale index in order to allow the following sale period's Coretime to be manipulated during the `LEADIN_PERIOD` prior to it becoming allocatable. +This map functions essentially as a linked list. With one region's `end` field functioning as the next region's key's `begin` field. It is keyed by the sale index in order to allow the following sale period's Coretime to be manipulated during the `LEADIN_PERIOD` prior to it becoming allocatable. An additional storage map is maintained to keep the "heads" of this linked list. It is called `NextRegion` and it maps `CoreIndex` to `Timeslice`, to indicate the earliest stored region of the given core. From 6a07fafe5aefbaf3345ed810487ec3eacf2ef80e Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 4 Jul 2023 16:51:30 +0200 Subject: [PATCH 09/44] Address some grumbles --- RFC-0001-Agile Coretime.md | 168 ++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 85 deletions(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 140defc..1a48958 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -2,12 +2,11 @@ | | | | --------------- | ------------------------------------------------------------------------------------------- | -| **Status** | Draft Proposal | -| **Areas** | General | +| **Start Date** | 30 June 2023 | +| **Status** | Draft | | **Description** | Agile periodic-sale-based model for assigning Coretime on the Polkadot Ubiquitous Computer. | -| **Issues** | n/a | | **Authors** | Gavin Wood | -| **Reviewers** | None | +| **Licence** | MIT/Apache2 | ## Summary @@ -32,6 +31,17 @@ There is no ability to determine capital requirements for hosting a parachain be However, quite possibly the most substantial problem is both a perceived and often real high barrier to entry of the Polkadot ecosystem. By forcing innovators to either raise 7-figure sums through investors or appeal to the wider token-holding community, Polkadot makes it essentially difficult for a small band of innovators from deploying their technology into Polkadot. While not being actually permissioned, it is also far from the barrierless, permissionless ideal which an innovation platform such as Polkadot should be striving for. +## Requirements + +1. The solution SHOULD provide an acceptable value-capture mechanism for the Polkadot network. +1. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions for the cost of ongoing deployment. +1. The solution SHOULD minimize the barriers to entry in the ecosystem. +1. The solution SHOULD work when the number of cores which the Polkadot UC can support changes over time. +1. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC. +1. The design SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. + +Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC would seem reasonable. + ## Stakeholders Primary stakeholder sets are: @@ -40,39 +50,52 @@ Primary stakeholder sets are: - Polkadot Parachain teams both present and future, and their users. - Polkadot DOT token holders. -_Facilitator:_ - -Parity Technologies' Ecosystem division. - -_Reviewers:_ - -None. - -_Consulted:_ -- Alistair -- Jonas -- Bjorn -- Rob H -- Rob K - _Socialization:_ This RFC was presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A small amount of socialisation at the Parachain Summit preceeded it and some substantial discussion followed it. Parity Ecosystem team is currently soliciting views from ecosystem teams who would be key stakeholders. -## Requirements +## Design -There are six main requirements: +### Overview -1. The solution MUST provide an acceptable value-capture mechanism for the Polkadot network. -2. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions. -3. The solution SHOULD minimize the barriers to entry in the ecosystem. -4. The solution SHOULD maximize the value which the Polkadot UC provides by allocating its limited resources optimally. -5. The design MUST work with a limited set of resources (cores on the Polkadot UC) whose properties and number may evolve over time. -6. The design MUST avoid creating additional dependency on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. This includes any dependency on the Relay-chain hosting a DOT token. +Upon implementation of this proposal, slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk* and *Instantaneous*. This proposal only mandates the implementation of *Bulk Coretime*; any mentions of *Instantaneous Coretime* is only intended to illustrate a possible final solution. -Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC would seem reasonable. +*Bulk Coretime* is sold periodically and allocated any time in advance of its usage, whereas *Instantaneous Coretime* is sold immediately prior to usage on a block-by-block basis with an explicit allocation at the time of purchase. -## Parameters +All Bulk Coretime sold by Polkadot is done so on a new system parachain known as the *Broker-chain*. Revenue from sales would be sent to the Polkadot Treasury. Owners of Bulk Coretime are tracked on this chain and the ownership status and properties (i.e. the specific period) of the owned Coretime are exposed over XCM as a non-fungible asset. + +At the request of the owner, the Broker-chain allows Bulk Coretime to be: + +1. Transferred in ownership. +2. Split into quantized, non-overlapping segments of Bulk Coretime with the same ownership. +3. Consumed in exchange for the allocation of a Core during its period. +4. Consumed in exchange for a pro-rata amount of the revenue from Instantaneous Core sales over its period. + +### Special Considerations + +As a migration mechanism, pre-existing leases (from the legacy lease/slots/crowdloan framework) SHALL be recorded in the Broker-chain and cores assigned to them. When the lease comes to expiry, there is a mechanism to gain a priority sale of Coretime to ensure that the Parachain suffers no downtime. + +Additionally, there is a renewal system which allows a core's Para assignment to be renewed unchanged with a known maximum price increase from month to month. Note that the intention behind renewals is only to ensure that committed Paras get some guarantees about price for predicting future costs. As such this price-capped renewal system only allows cores to be reused for their same tasks from month to month. In any other context, the regular sale system must be used and the regular price paid. + +### Relay-chain and Instantaneous Coretime + +Sales of Instantaneous Coretime are expected to happen on the Polkadot Relay-chain. The Relay-chain is expected to be responsible for: + +- holding non-transferable, non-refundable DOT balance information for collators. +- setting and adjusting the price of Instantaneous Coretime based on usage. +- allowing collators to consume their DOT balance in exchange for the ability to schedule one PoV for near-immediate usage. +- ensuring the Broker-chain has timely book-keeping information on Coretime sales revenue. This should include a total instantaneous revenue amount for each block number. + +### Broker-chain + +The Broker-chain is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with scheduling information of: + +- The number of cores which should be made available during the next session. +- Which cores should be allocated to which para IDs. + +Any cores left unallocated are assumed by the Broker-chain to be used for Instantaneous Coretime sales. + +### Parameters This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, either *suggested* in which case it is non-binding and the proposal should not be judged on the value since the governance mechanism of Polkadot is expected to specify/maintain it; or *specified* in which case the proposal should be judged on the merit of the value as-is. @@ -85,44 +108,6 @@ This proposal includes a number of parameters which need not necessarily be fixe | `LEADIN_PERIOD` | `14 * DAYS` | | `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | - -## Design - -### Overview - -Upon implementation of this proposal, slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk* and *Instantaneous*. This proposal only mandates the implementation of *Bulk Coretime*; any mentions of *Instantaneous Coretime* should be treated only in terms of recommendation. - -*Bulk Coretime* is sold periodically and allocated any time in advance of its usage, whereas *Instantaneous Coretime* is sold immediately prior to usage on a block-by-block basis with an explicit allocation at the time of purchase. - -All Bulk Coretime sold by Polkadot is done so on a new system parachain known as the *Broker-chain*. Owners of Bulk Coretime are tracked on this chain and the ownership status and properties (i.e. the specific period) of the owned Coretime are exposed over XCM as a non-fungible asset. - -At the request of the owner, the Broker-chain allows Bulk Coretime to be: - -1. Transferred in ownership. -2. Split into quantized, non-overlapping segments of Bulk Coretime with the same ownership. -3. Consumed in exchange for the allocation of a Core during its period. -4. Consumed in exchange for a pro-rata amount of the revenue from Instantaneous Core sales over its period. - -Pre-existing leases SHALL be recorded in the Broker-chain and cores reserved for them. - -Sales of Instantaneous Coretime is expected to happen on the Polkadot Relay-chain. The Relay-chain is expected to be responsible for: - -- holding non-transferable, non-refundable DOT balance information for collators. -- setting and adjusting the price of Instantaneous Coretime based on usage. -- allowing collators to consume their DOT balance in exchange for the ability to schedule one PoV for near-immediate usage. -- ensuring the Broker-chain has timely book-keeping information on Coretime sales revenue. This should include a total instantaneous revenue amount for each block number. - -### Broker-chain - -The Broker-chain is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with scheduling information of: - -- How many cores which should be made available during the next session. -- Which cores should be allocated to which para IDs. - -Any cores left unallocated are assumed by the Broker-chain to be used for Instantaneous Coretime sales. - -It is also expected to receive information from the Relay-chain on total revenue from instantaneous coretime sales on a per-block basis. - ### Bulk Sales There is a periodic sale of Coretime happening at a period of `BULK_PERIOD`. A number of instances of Coretime spanning from `LEADIN_PERIOD` in the future to `LEADIN_PERIOD + BULK_PERIOD` in the future of a single Polkadot UC Core is offered by the Polkadot System at a fixed price. @@ -231,6 +216,25 @@ This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly lik AllowedRenewals map is updated with this information ready for the following Bulk sale. +### Notes on Price Setting + +The specific price setting mechanism is out of scope for this proposal and should be covered in some other work. The present proposal assumes the existence of a price-setting mechanism which could take into account three parameters; two mostly fixed and one which changes each `BULK_PERIOD`. These parameters are `BULK_TARGET`, `BULK_LIMIT` and `CORES_SOLD` which is the actual number of cores sold for the present `BULK_PERIOD` and is always an unsigned integer at most `BULK_LIMIT`. + +In general we would expect the price to increase the closer `CORES_SOLD` gets to `BULK_LIMIT` and to decrease the closer it gets to zero. If it is exactly equal to `BULK_TARGET`, then we would expect the price to remain the same. + +A simple example of this would be the formula: + +``` +NEW_PRICE := IF CORES_OLD < BULK_TARGET THEN + OLD_PRICE - OLD_PRICE / 2 * CORES_SOLD / BULK_TARGET +ELSE + OLD_PRICE + OLD_PRICE / 2 * + (CORES_SOLD - BULK_TARGET) / (BULK_LIMIT - BULK_TARGET) +END IF +``` + +This exists only as a trivial example to demonstrate a basic solution exists, and should not be intended as a concrete proposal. + ### Notes on Instantaneous Core Sales For access to the Instantaneous Core sales we may include an `allocate_instantaneous` function. This should allocate the Coretime for usage by Polkadot to serve instantaneous requests and allow the `owner` to collect a pro-rata amount of total Instantaneous sales revenue. @@ -252,40 +256,34 @@ Implementation of this proposal comes in several phases: 7. Governance upgrade proposal(s). 8. Monitoring of the upgrade process. -## Performance +## Performance, Ergonomics and Compatibility -This proposal has no immediate performance considerations. - -## Ergonomics - -This proposal makes no changes to the existing end-user experience. - -## Backwards Compatibility +No specific considerations. Parachains already deployed into the Polkadot UC MUST have a clear plan of action to migrate to an agile Coretime market. -## Security considerations +While this proposal does not introduce documentable features per se, adequate documentation must be provided to potential purchasers of Polkadot Coretime. This SHOULD include any alterations to the Polkadot-SDK software collection, most likely Cumulus. + +## Testing, Security and Privacy + +Regular testing through unit tests, integration tests, manual testnet tests, zombie-net tests and fuzzing SHOULD be conducted. A regular security review SHOULD be conducted prior to deployment through a review by the Web3 Foundation economic research group. Any final implementation MUST pass a professional external security audit. -## Privacy considerations - The proposal introduces no new privacy concerns. -## Testing +## Future Directions and Related Material -Regular testing through unit tests, integration tests, manual testnet tests, zombie-net tests and fuzzing SHOULD be conducted. +RFC-2 proposes a means of implementing the high-level allocations within the Relay-chain. -## Documentation +Additional work should specify the interface for the instantaneous market revenue so that the Broker chain can ensure Bulk Coretime placed in the instantaneous market is properly compensated. -While this proposal does not introduce documentable features per se, adequate documentation must be provided to potential purchasers of Polkadot Coretime. This SHOULD include any alterations to the Polkadot-SDK software collection, most likely Cumulus. - -## Drawbacks, alternatives, and unknowns +## Drawbacks, Alternatives and Unknowns None at present. -## Prior art and references +## Prior Art and References None. From eb8a68141c4bb7ddf43201d049012bf2bfccb336 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 4 Jul 2023 17:01:39 +0200 Subject: [PATCH 10/44] Weighted allocations --- RFC-0001-Agile Coretime.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 5a9f191..8a16f85 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -38,7 +38,7 @@ However, quite possibly the most substantial problem is both a perceived and oft 1. The solution SHOULD minimize the barriers to entry in the ecosystem. 1. The solution SHOULD work when the number of cores which the Polkadot UC can support changes over time. 1. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC. -1. The design SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. +1. The solution SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC would seem reasonable. @@ -106,7 +106,7 @@ This proposal includes a number of parameters which need not necessarily be fixe | `BULK_TARGET` | `30` | | `BULK_LIMIT` | `45` | | `LEADIN_PERIOD` | `14 * DAYS` | -| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | +| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | ### Bulk Sales @@ -134,7 +134,7 @@ struct RegionRecord { owner: AccountId, end: Timeslice, price: Option, - allocation: Option>, // begins set to `None` + allocation: Option>, // begins set to `None` } ``` @@ -186,7 +186,7 @@ If the `RegionRecord`'s `price` field is `Some` (indicating that the Region is f ```rust struct RenewalRecord { - target: Vec, + target: Vec<(ParaId, u16)>, price: Balance, sale: SaleIndex, } From 2538bb058113c671a4b6edd962732d025ed53049 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 4 Jul 2023 17:39:23 +0200 Subject: [PATCH 11/44] Relay-chain API --- RFC-0001-Agile Coretime.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 8a16f85..9d46255 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -243,6 +243,15 @@ For an efficient market to form around the provision of Bulk-purchased Cores int In order to ensure this, then it is crucial that Instantaneous Coretime, once purchased, cannot be held indefinitely prior to eventual use since, if this were the case, a nefarious collator could purchase Coretime when cheap and utilize it some time later when expensive and deprive private Coretime providers of their revenue. It SHOULD therefore be assumed that Instantaneous Coretime, once purchased, has a definite and short "shelf-life", after which it becomes unusable. This incentivizes collators to avoid purchasing Coretime unless they expect to utilize it imminently and thus helps create an efficient market-feedback mechanism whereby a higher price will actually result in material revenues for private Coretime providers who contribute to the pool of Cores available to service Instantaneous Coretime purchases. +### The Relay-chain API + +The Relay-chain provides a pallet-based API for the Broker-chain to utilize to inform it in real-time of allocation information for the cores and also to alter the number of cores in the next session. There are two functions which it should expose: + +- `fn set_core_count(count: u16)`: Sets the number of active cores from the next session onwards, identified from index `0` to index `count - 1` inclusive. +- `fn assign_core(core_index: u16, assignment: Option>, begin: BlockNumber, end_hint: Option)`: Requests the Relay-chain to provision core identified by `core_index` to process paras identified within the `assignment` vector relatively biased according to the accompanying `u16` *weight* parameter. The weight parameter indicates the ratio of blocks which *on average* should be being processed for the the given para compared to the other paras mentioned in the `assignment` vector. + +The Relay-chain should publish, through a well-known storage key, the number of blocks in advance which `assign_core` should be called to ensure that the core gets scheduled properly. We can denote this quantity `ADVANCE_NOTICE`. The Relay-chain MUST respect the core assignment provided that `assign_core` gets processed on a block whose number is no greater than `begin - ADVANCE_NOTICE`. + ## Implementation Implementation of this proposal comes in several phases: From e95b2e94f28e08799e5aba02d575bda0283dce17 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 6 Jul 2023 12:43:11 +0200 Subject: [PATCH 12/44] Bitmapping --- RFC-0001-Agile Coretime.md | 98 ++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 10 deletions(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 9d46255..1f197d6 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -122,23 +122,101 @@ If there are more purchase requests than available cores for purchase in this pe When a region of Bulk Coretime is initially issued through this purchase, the price it was purchased for is recorded, in addition to the beginning and end Relay-chain block numbers to which it corresponds. -The Broker-chain SHALL record this information in a storage double-map called Regions, keyed first by the current bulk `SaleIndex` (a `u16` starting at zero and incrementing with each sale), then secondarily by a `RegionId`. It shall map into a value of `RegionRecord`: +The Broker-chain SHALL record this information in a storage map called Regions, keyed by a `RegionId`. It shall map into a value of `RegionRecord`: ```rust -type SaleIndex = u16; +enum RecordState { + Fresh { owner: AccountId }, + Instantaneous { beneficiary: AccountId }, + Assigned { target: ParaId }, +} +type CoreIndex = u16; +type CorePart = (u16, u64); // 80-bit bitmap. +type Timeslice = u32; // 80 block amounts. struct RegionId { - core: CoreIndex, // A `u16`. - begin: Timeslice, // A `u16`. + core: (CoreIndex, CorePart), + begin: Timeslice, } struct RegionRecord { - owner: AccountId, end: Timeslice, - price: Option, - allocation: Option>, // begins set to `None` + state: RecordState, } +map RegionId => RegionRecord; + +// Simple example with a `u16` `CorePart` and bulk sold in 100 timeslices. +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 0 } => + RegionRecord { end: 100u32, state: Fresh(Alice) }; +// First split @ 50 +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Alice) }; +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => + RegionRecord { end: 100u32, state: Fresh(Alice) }; +// Share half of first 50 blocks +RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Alice) }; +RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Alice) }; +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => + RegionRecord { end: 100u32, state: Fresh(Alice) }; +// Sell half of them to Bob +RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Alice) }; +RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Bob) }; +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => + RegionRecord { end: 100u32, state: Fresh(Alice) }; +// Bob splits first 10 and assigns them to himself. +RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Alice) }; +RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 0 } => + RegionRecord { end: 10u32, state: Fresh(Bob) }; +RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 10 } => + RegionRecord { end: 50u32, state: Fresh(Bob) }; +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => + RegionRecord { end: 100u32, state: Fresh(Alice) }; +// Bob shares first 10 3 ways and sells smaller shares to Charlie and Dave +RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => + RegionRecord { end: 50u32, state: Fresh(Alice) }; +RegionId { core: (0u16, 0b0000_0000_1100_0000u16), begin: 0 } => + RegionRecord { end: 10u32, state: Fresh(Charlie) }; +RegionId { core: (0u16, 0b0000_0000_0011_0000u16), begin: 0 } => + RegionRecord { end: 10u32, state: Fresh(Dave) }; +RegionId { core: (0u16, 0b0000_0000_0000_1111u16), begin: 0 } => + RegionRecord { end: 10u32, state: Fresh(Bob) }; +RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 10 } => + RegionRecord { end: 50u32, state: Fresh(Bob) }; +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => + RegionRecord { end: 100u32, state: Fresh(Alice) }; +// Bob assigns to his para B, Charlie and Dave assign to their paras C and D; Alice assigns to A +RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => + RegionRecord { end: 50u32, state: Assigned(A) }; +RegionId { core: (0u16, 0b0000_0000_1100_0000u16), begin: 0 } => + RegionRecord { end: 10u32, state: Assigned(C) }; +RegionId { core: (0u16, 0b0000_0000_0011_0000u16), begin: 0 } => + RegionRecord { end: 10u32, state: Assigned(D) }; +RegionId { core: (0u16, 0b0000_0000_0000_1111u16), begin: 0 } => + RegionRecord { end: 10u32, state: Assigned(B) }; +RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 10 } => + RegionRecord { end: 50u32, state: Assigned(B) }; +RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => + RegionRecord { end: 100u32, state: Assigned(A) }; +// Actual notifications to relay chain. +// Assumes: +// - Timeslice is 10 blocks. +// - Timeslice 0 begins at block #1000. +// - Relay needs 10 blocks notice of change. +// +// Block 990: +reassign_core(core: 0u16, begin: 1000, assignment: vec![(A, 8), (C, 2), (D, 2), (B, 4)]) +// Block 1090: +reassign_core(core: 0u16, begin: 1100, assignment: vec![(A, 8), (B, 8)]) +// Block 1490: +reassign_core(core: 0u16, begin: 1100, assignment: vec![(A, 16)]) ``` -This map functions essentially as a linked list. With one region's `end` field functioning as the next region's key's `begin` field. It is keyed by the sale index in order to allow the following sale period's Coretime to be manipulated during the `LEADIN_PERIOD` prior to it becoming allocatable. + + +This map functions essentially as a linked list, with one region's `end` field acting as a reference to the next region's key's `begin` field. It is keyed by the sale index in order to allow the following sale period's Coretime to be manipulated during the `LEADIN_PERIOD` prior to it becoming allocatable. An additional storage map is maintained to keep the "heads" of this linked list. It is called `NextRegion` and it maps `CoreIndex` to `Timeslice`, to indicate the earliest stored region of the given core. @@ -186,7 +264,7 @@ If the `RegionRecord`'s `price` field is `Some` (indicating that the Region is f ```rust struct RenewalRecord { - target: Vec<(ParaId, u16)>, + target: Vec<(ParaId, u8)>, price: Balance, sale: SaleIndex, } @@ -248,7 +326,7 @@ In order to ensure this, then it is crucial that Instantaneous Coretime, once pu The Relay-chain provides a pallet-based API for the Broker-chain to utilize to inform it in real-time of allocation information for the cores and also to alter the number of cores in the next session. There are two functions which it should expose: - `fn set_core_count(count: u16)`: Sets the number of active cores from the next session onwards, identified from index `0` to index `count - 1` inclusive. -- `fn assign_core(core_index: u16, assignment: Option>, begin: BlockNumber, end_hint: Option)`: Requests the Relay-chain to provision core identified by `core_index` to process paras identified within the `assignment` vector relatively biased according to the accompanying `u16` *weight* parameter. The weight parameter indicates the ratio of blocks which *on average* should be being processed for the the given para compared to the other paras mentioned in the `assignment` vector. +- `fn assign_core(core_index: u16, assignment: Option>, begin: BlockNumber, end_hint: Option)`: Requests the Relay-chain to provision core identified by `core_index` to process paras identified within the `assignment` vector relatively biased according to the accompanying `u16` *weight* parameter. The weight parameter indicates the ratio of blocks which *on average* should be being processed for the the given para compared to the other paras mentioned in the `assignment` vector. The Relay-chain should publish, through a well-known storage key, the number of blocks in advance which `assign_core` should be called to ensure that the core gets scheduled properly. We can denote this quantity `ADVANCE_NOTICE`. The Relay-chain MUST respect the core assignment provided that `assign_core` gets processed on a block whose number is no greater than `begin - ADVANCE_NOTICE`. From 015f92a9dcb26c05c7033505e194d28082b5d002 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 7 Jul 2023 19:25:15 +0200 Subject: [PATCH 13/44] Core Parts --- RFC-0001-Agile Coretime.md | 452 +++++++++++++++++++++++-------------- 1 file changed, 281 insertions(+), 171 deletions(-) diff --git a/RFC-0001-Agile Coretime.md b/RFC-0001-Agile Coretime.md index 1f197d6..eb21560 100644 --- a/RFC-0001-Agile Coretime.md +++ b/RFC-0001-Agile Coretime.md @@ -3,10 +3,8 @@ | | | | --------------- | ------------------------------------------------------------------------------------------- | | **Start Date** | 30 June 2023 | -| **Status** | Draft | | **Description** | Agile periodic-sale-based model for assigning Coretime on the Polkadot Ubiquitous Computer. | | **Authors** | Gavin Wood | -| **Licence** | MIT/Apache2 | ## Summary @@ -34,13 +32,13 @@ However, quite possibly the most substantial problem is both a perceived and oft ## Requirements 1. The solution SHOULD provide an acceptable value-capture mechanism for the Polkadot network. -1. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions for the cost of ongoing deployment. +1. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions for the cost of ongoing deployment. 1. The solution SHOULD minimize the barriers to entry in the ecosystem. 1. The solution SHOULD work when the number of cores which the Polkadot UC can support changes over time. -1. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC. +1. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC, including by facilitating the trade of regular core assignment at various intervals and for various spans. 1. The solution SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. -Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC would seem reasonable. +Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC should not be unreasonable. ## Stakeholders @@ -54,240 +52,187 @@ _Socialization:_ This RFC was presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A small amount of socialisation at the Parachain Summit preceeded it and some substantial discussion followed it. Parity Ecosystem team is currently soliciting views from ecosystem teams who would be key stakeholders. -## Design +## Explanation ### Overview -Upon implementation of this proposal, slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk* and *Instantaneous*. This proposal only mandates the implementation of *Bulk Coretime*; any mentions of *Instantaneous Coretime* is only intended to illustrate a possible final solution. +Upon implementation of this proposal, slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk* and *Instantaneous*. This proposal only mandates the implementation of *Bulk Coretime*; any mentions of *Instantaneous Coretime* are only intended to illustrate a possible final solution. -*Bulk Coretime* is sold periodically and allocated any time in advance of its usage, whereas *Instantaneous Coretime* is sold immediately prior to usage on a block-by-block basis with an explicit allocation at the time of purchase. +Bulk Coretime is sold periodically on a specialised *Brokerage System Chain* and allocated in advance of its usage, whereas Instantaneous Coretime is sold on the Relay-chain immediately prior to usage on a block-by-block basis with an explicit allocation at the time of purchase. -All Bulk Coretime sold by Polkadot is done so on a new system parachain known as the *Broker-chain*. Revenue from sales would be sent to the Polkadot Treasury. Owners of Bulk Coretime are tracked on this chain and the ownership status and properties (i.e. the specific period) of the owned Coretime are exposed over XCM as a non-fungible asset. +Revenue from sales of Bulk Coretime is owned by the Polkadot Treasury. Owners of Bulk Coretime are tracked on this chain and the ownership status and properties of the owned Coretime are exposed over XCM as a non-fungible asset. -At the request of the owner, the Broker-chain allows Bulk Coretime to be: +At the request of the owner, the Broker-chain allows a single Bulk Coretime asset, known as a *Region*, to be used in various ways including transferal to another owner, allocated to a particular parachain or placed in the Instantaneous Coretime Pool. Regions can also be split out, either into non-overlapping sub-spans or exactly-overlapping spans with less regularity. -1. Transferred in ownership. -2. Split into quantized, non-overlapping segments of Bulk Coretime with the same ownership. -3. Consumed in exchange for the allocation of a Core during its period. -4. Consumed in exchange for a pro-rata amount of the revenue from Instantaneous Core sales over its period. +The Brokerage System Chain periodically instructs the Relay-chain to assign its cores to alternative tasks as and when the allocation changes owing to a new Region taking effect. -### Special Considerations +#### Special Considerations -As a migration mechanism, pre-existing leases (from the legacy lease/slots/crowdloan framework) SHALL be recorded in the Broker-chain and cores assigned to them. When the lease comes to expiry, there is a mechanism to gain a priority sale of Coretime to ensure that the Parachain suffers no downtime. +As a migration mechanism, pre-existing leases (from the legacy lease/slots/crowdloan framework) SHALL be recorded in the Broker-chain and cores assigned to them. When the lease comes to expiry, there is a mechanism to gain a priority sale of Bulk Coretime to ensure that the Parachain suffers no downtime. -Additionally, there is a renewal system which allows a core's Para assignment to be renewed unchanged with a known maximum price increase from month to month. Note that the intention behind renewals is only to ensure that committed Paras get some guarantees about price for predicting future costs. As such this price-capped renewal system only allows cores to be reused for their same tasks from month to month. In any other context, the regular sale system must be used and the regular price paid. +Additionally, there is a renewal system which allows a core's assignment to be renewed unchanged with a known maximum price increase from month to month. In this case, the core's assignment must not include an Instantaneous Coretime allocation and must not have been split into smaller amounts. -### Relay-chain and Instantaneous Coretime +Note that the intention behind renewals is only to ensure that committed Paras get some guarantees about price for predicting future costs. As such this price-capped renewal system only allows cores to be reused for their same tasks from month to month. In any other context, the regular sale system must be used and the regular price paid. -Sales of Instantaneous Coretime are expected to happen on the Polkadot Relay-chain. The Relay-chain is expected to be responsible for: +#### Relay-chain and Instantaneous Coretime -- holding non-transferable, non-refundable DOT balance information for collators. +Sales of Instantaneous Coretime happen on the Polkadot Relay-chain. The Relay-chain is expected to be responsible for: + +- holding non-transferable, non-refundable DOT-denominated Instantaneous Coretime Market Credit balance information for collators. - setting and adjusting the price of Instantaneous Coretime based on usage. -- allowing collators to consume their DOT balance in exchange for the ability to schedule one PoV for near-immediate usage. -- ensuring the Broker-chain has timely book-keeping information on Coretime sales revenue. This should include a total instantaneous revenue amount for each block number. +- allowing collators to consume their ICM Credit at the current pricing in exchange for the ability to schedule one PoV for near-immediate usage. +- ensuring the Broker System Chain has timely accounting information on Coretime sales revenue. -### Broker-chain +#### Broker System Chain -The Broker-chain is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with scheduling information of: +Also known as the *Broker-chain*, this is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with scheduling information of: - The number of cores which should be made available during the next session. -- Which cores should be allocated to which para IDs. +- Which para IDs should be running on which cores and in what ratios. -Any cores left unallocated are assumed by the Broker-chain to be used for Instantaneous Coretime sales. +The specific interface is properly described in RFC-5. -### Parameters +### Detail -This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, either *suggested* in which case it is non-binding and the proposal should not be judged on the value since the governance mechanism of Polkadot is expected to specify/maintain it; or *specified* in which case the proposal should be judged on the merit of the value as-is. +#### Parameters -| Name | Value | -| ------------------- | ---------------------------- | -| `BULK_PERIOD` | `28 * DAYS` | -| `TIMESLICE` | `10 * MINUTES` | -| `BULK_TARGET` | `30` | -| `BULK_LIMIT` | `45` | -| `LEADIN_PERIOD` | `14 * DAYS` | -| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | +This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, and are either *suggested* or *specified*. If *suggested*, it is non-binding and the proposal should not be judged on the value since the governance mechanism of Polkadot is expected to specify/maintain it. If *specified*, then the proposal should be judged on the merit of the value as-is. -### Bulk Sales +| Name | Value | | +| ------------------- | ---------------------------- | ---------- | +| `BULK_PERIOD` | `28 * DAYS` | specified | +| `LEADIN_PERIOD` | `14 * DAYS` | specified | +| `TIMESLICE` | `8 * MINUTES` | specified | +| `BULK_TARGET` | `30` | suggested | +| `BULK_LIMIT` | `45` | suggested | +| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | suggested | -There is a periodic sale of Coretime happening at a period of `BULK_PERIOD`. A number of instances of Coretime spanning from `LEADIN_PERIOD` in the future to `LEADIN_PERIOD + BULK_PERIOD` in the future of a single Polkadot UC Core is offered by the Polkadot System at a fixed price. +#### Reservations -These instances which are owned by purchaser are called *regions*. This sale happens on the Broker-chain. Regions are quantized into atomic periods of `TIMESLICE`, into which `BULK_PERIOD` divides a whole number of times. The `Timeslice` type is a `u16` which can be multiplied by `TIMESLICE` to give a `BlockNumber` value indicating the same period in times of (Relay-chain) blocks. +The Broker-chain includes some governance-set reservations of Coretime; these cover every System-chain as well as the pre-existing leased chains. -The Broker-chain aims to sell some `BULK_TARGET` of Cores, up to some `BULK_LIMIT`. It makes this sale in a single batch `LEADIN_PERIOD` prior to the beginning of the period being sold. The Broker chain publishes a price for this batch of sales for the `BULK_TARGET` period prior to the sale execution. +#### Regions -Accounts may call a `purchase` function with the appropriate funds in place to have their funds reserved and signal an intention to purchase Bulk Coretime for the forthcoming period. One account may have only one pending purchase. Any number of accounts may attempt to `purchase` Bulk Coretime for the forthcoming period, but the order is recorded. +A *Region* is an assignable period of Coretime with a known regularity. -If there are more purchase requests than available cores for purchase in this period, then requests are processed in incoming order. Any additional purchase orders are carried over but marked as such. A purchase is only cancellable if it was carried over. +All Regions are associated with a unique *Core Index*, to identify which core the Region controls the assignment of. -When a region of Bulk Coretime is initially issued through this purchase, the price it was purchased for is recorded, in addition to the beginning and end Relay-chain block numbers to which it corresponds. +All Regions are also associated with a *Core Parts*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Parts value, it is said to be *Complete*. -The Broker-chain SHALL record this information in a storage map called Regions, keyed by a `RegionId`. It shall map into a value of `RegionRecord`: +All Regions have a span. Region spans are quantized into periods of `TIMESLICE` blocks; `BULK_PERIOD` divides into `TIMESLICE` a whole number of times. -```rust -enum RecordState { - Fresh { owner: AccountId }, - Instantaneous { beneficiary: AccountId }, - Assigned { target: ParaId }, -} -type CoreIndex = u16; -type CorePart = (u16, u64); // 80-bit bitmap. -type Timeslice = u32; // 80 block amounts. -struct RegionId { - core: (CoreIndex, CorePart), - begin: Timeslice, -} -struct RegionRecord { - end: Timeslice, - state: RecordState, -} -map RegionId => RegionRecord; +The `Timeslice` type is a `u32` which can be multiplied by `TIMESLICE` to give a `BlockNumber` value representing the same quantity in terms of (Relay-chain) blocks. -// Simple example with a `u16` `CorePart` and bulk sold in 100 timeslices. -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 0 } => - RegionRecord { end: 100u32, state: Fresh(Alice) }; -// First split @ 50 -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Alice) }; -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => - RegionRecord { end: 100u32, state: Fresh(Alice) }; -// Share half of first 50 blocks -RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Alice) }; -RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Alice) }; -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => - RegionRecord { end: 100u32, state: Fresh(Alice) }; -// Sell half of them to Bob -RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Alice) }; -RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Bob) }; -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => - RegionRecord { end: 100u32, state: Fresh(Alice) }; -// Bob splits first 10 and assigns them to himself. -RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Alice) }; -RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 0 } => - RegionRecord { end: 10u32, state: Fresh(Bob) }; -RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 10 } => - RegionRecord { end: 50u32, state: Fresh(Bob) }; -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => - RegionRecord { end: 100u32, state: Fresh(Alice) }; -// Bob shares first 10 3 ways and sells smaller shares to Charlie and Dave -RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => - RegionRecord { end: 50u32, state: Fresh(Alice) }; -RegionId { core: (0u16, 0b0000_0000_1100_0000u16), begin: 0 } => - RegionRecord { end: 10u32, state: Fresh(Charlie) }; -RegionId { core: (0u16, 0b0000_0000_0011_0000u16), begin: 0 } => - RegionRecord { end: 10u32, state: Fresh(Dave) }; -RegionId { core: (0u16, 0b0000_0000_0000_1111u16), begin: 0 } => - RegionRecord { end: 10u32, state: Fresh(Bob) }; -RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 10 } => - RegionRecord { end: 50u32, state: Fresh(Bob) }; -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => - RegionRecord { end: 100u32, state: Fresh(Alice) }; -// Bob assigns to his para B, Charlie and Dave assign to their paras C and D; Alice assigns to A -RegionId { core: (0u16, 0b1111_1111_0000_0000u16), begin: 0 } => - RegionRecord { end: 50u32, state: Assigned(A) }; -RegionId { core: (0u16, 0b0000_0000_1100_0000u16), begin: 0 } => - RegionRecord { end: 10u32, state: Assigned(C) }; -RegionId { core: (0u16, 0b0000_0000_0011_0000u16), begin: 0 } => - RegionRecord { end: 10u32, state: Assigned(D) }; -RegionId { core: (0u16, 0b0000_0000_0000_1111u16), begin: 0 } => - RegionRecord { end: 10u32, state: Assigned(B) }; -RegionId { core: (0u16, 0b0000_0000_1111_1111u16), begin: 10 } => - RegionRecord { end: 50u32, state: Assigned(B) }; -RegionId { core: (0u16, 0b1111_1111_1111_1111u16), begin: 50 } => - RegionRecord { end: 100u32, state: Assigned(A) }; -// Actual notifications to relay chain. -// Assumes: -// - Timeslice is 10 blocks. -// - Timeslice 0 begins at block #1000. -// - Relay needs 10 blocks notice of change. -// -// Block 990: -reassign_core(core: 0u16, begin: 1000, assignment: vec![(A, 8), (C, 2), (D, 2), (B, 4)]) -// Block 1090: -reassign_core(core: 0u16, begin: 1100, assignment: vec![(A, 8), (B, 8)]) -// Block 1490: -reassign_core(core: 0u16, begin: 1100, assignment: vec![(A, 16)]) -``` +Regions can be tasked to a ParaId or placed in the Instantaneous Coretime Pool. If they have yet to be placed or tasked, then they are fresh. Fresh Regions have an *Owner*. +#### Bulk Sales +A sale of Bulk Coretime occurs on the Broker Chain every `BULK_PERIOD` blocks. -This map functions essentially as a linked list, with one region's `end` field acting as a reference to the next region's key's `begin` field. It is keyed by the sale index in order to allow the following sale period's Coretime to be manipulated during the `LEADIN_PERIOD` prior to it becoming allocatable. +In every sale, a `BULK_LIMIT` of individual *Regions* are offered for sale at a particular Sale Price. -An additional storage map is maintained to keep the "heads" of this linked list. It is called `NextRegion` and it maps `CoreIndex` to `Timeslice`, to indicate the earliest stored region of the given core. +The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin `LEADIN_PERIOD` blocks into the future at the time of the sale. -Notably, if a region is split or transferred, then the `price` is reset to `None`, which has the effect of disallowing a price-increase-capped renewal. +The Regions offered for sale also have complete -The Broker-chain provides feedback to the Relay-chain on which `ParaId` sets should be serviced on which cores, and does so as they change. Each block, the Broker-chain checks if the period of a `Timeslice` has elapsed. If so, it checks to see if any cores have a newly active `RegionRecord` value in the `Regions` map. If they do, it MUST notify the Relay-chain of the new responsibilities of the relevant `core`. In this case, it MUST remove the item from the `Regions` map and update the `NextRegion` map so it maps the relevant core to the value of removed record's `end` field. +Each Region offered for sale has a different Core Index, ensuring that they each represent an independently allocatable resource on the Polkadot UC. -If the `RegionRecord` value for an elapsed `RegionId` has an `allocation` of `None`, then the item is not removed and the Relay-chain is instructed to place the core for instantaneous use. +After every sale, the Next Sale Price is set according to the Previous Sale Price and the number of Regions sold compared to the desired and maximum amount of Regions to be sold. See Price Setting for additional detail. + +This is designed to give a minimum of around two weeks worth of time for it to be sliced, shared, traded and allocated. + +The Broker-chain aims to sell some `BULK_TARGET` month-long Regions, up to some maximum amount of `BULK_LIMIT`. It makes this sale in a single batch prior to the beginning of the period being sold by `LEADIN_PERIOD` blocks. The Broker-chain publishes the price for a Region prior to the sale execution. + +#### Purchasing + +Accounts may call a `purchase` function with the appropriate funds in place to have their funds placed On Hold and signal an commitment to purchase Bulk Coretime for the forthcoming period. One account may have only one pending purchase. Any number of accounts may attempt to `purchase` Bulk Coretime for the forthcoming period and this order is recorded. + +Requests are processed in incoming order. If there are more purchase requests than available Regions for purchase then remaining purchase orders are carried over to the next sale and flagged as carried. A purchase is only cancellable (and the funds On Hold refundable) if it is so flagged. + +When a purchase request is processed, the Funds On Hold are transferred to the local Treasury and a fresh Region of the proper span is issued and assigned to the purchaser as the owner. + +#### Manipulation + +Regions may be manipulated in various ways by its owner: + +1. *Transferred* in ownership. +1. *Assigned* to a single, specific ParaID task. +1. *Partitioned* into quantized, non-overlapping segments of Bulk Coretime with the same ownership. +1. *Interlaced* into multiple Regions over the same period whose eventual assignments take turns to be scheduled. +1. *Contributed* into the Instantaneous Coretime Pool, in return for a pro-rata amount of the revenue from the Instantaneous Coretime Sales over its period. + +#### Enactment ### Specific functions of the Broker-chain Several functions of the Broker-chain SHALL be exposed through dispatchables and/or a `nonfungible` trait implementation integrated into XCM: -#### 1. Transfer of ownership +#### 1. `transfer` -A `transfer(region: RegionId, new_owner: AccountId)` dispatchable SHALL have the effect of altering the current `owner` field of `region` in the `Regions` map from the signed origin to `new_owner`. +Regions may have their ownership transferred. + +A `transfer(region: RegionId, new_owner: AccountId)` dispatchable SHALL have the effect of altering the current owner of the Region identified by `region` from the signed origin to `new_owner`. An implementation of the `nonfungible` trait SHOULD include equivalent functionality. `RegionId` SHOULD be used for the `AssetInstance` value. -In both cases, the `price` field SHALL become `None`. +#### 2. `assign` -#### 2. Split into segments +Regions may be consumed in exchange for being assigned to a core. -A `split(region: RegionId, pivot: Timeslice)` dispatchable SHALL have the effect of mutating the Regions entry of key `region` so that the `end` field becomes `pivot` and create a new Regions entry of the same `core` but a `begin` equal to `pivot` whose `RegionRecord` has the same `owner`, `end` and `allocation` fields as the origin value of `region`. +A dispatchable `assign(region: RegionId, target: ParaId)` SHALL be provided corresponding to the `allocate` function described above. -`price` in both records is/becomes `None`. +It MUST be called with a Signed origin equal to the `owner` field of the value of the Regions map for the `region` key. The `allocation` field of this value MUST be `None` (a region may not be re-allocated). + +On success, the `assign` value is changed to `Some` whose inner value is equal to `target`. + +If the `begin` field of the `region` parameter is less than the current `Timeslice` value, then it is removed and re-entered with the current `Timeslice` value plus one, unless this would be equal to or greater than the `end` field of the corresponding `RegionRecord` value. + +If the Region's span is the entire `BULK_PERIOD`, then the Broker-chain records in storage that the allocation happened during this period in order to facilitate the possibility for a renewal. + +#### 3. `partition` + +Regions may be split apart into two non-overlapping interior Regions of the same regularity. + +A `partition(region: RegionId, pivot: Timeslice)` dispatchable SHALL have the effect of removing the Region identified by `region` and adding two new Regions of the same owner and core-parts. One new Region will begin at the same point of the old Region but end at `pivot`, whereas the other will begin at `pivot` and end at the end point of the old Region. Also: - `owner` field of `region` must the equal to the Signed origin. - `pivot` must equal neither the `begin` nor `end` fields of the `region`. -#### 3. Consumed in exchange for allocation +#### 4. `interlace` -A dispatchable `allocate(region: RegionId, target: Vec)` SHALL be provided corresponding to the `allocate` function described above. +Regions may be strided into two Regions of the same span whose eventual assignments take turns on the core. -It MUST be called with a Signed origin equal to the `owner` field of the value of the Regions map for the `region` key. The `allocation` field of this value MUST be `None` (a region may not be re-allocated). +An `interlace(region: RegionId, parts: CoreParts)` dispatchable SHALL have the effect of removing the Region identified by `region` and create two new Regions. The new Regions will each have the same span and owner of the old Region, but one Region will have Core Parts equal to `parts` and the other will have Core Parts equal to the XOR of `parts` and the Core Parts of the old Region. -On success, the `allocation` value is changed to `Some` whose inner value is equal to `target`. +Also: +- `owner` field of `region` must the equal to the Signed origin. +- `parts` must have some bits set AND must not equal the Core Parts of the old Region AND must only have bits set which are also set in the old Region's' Core Parts. -If the `begin` field of the `region` parameter is less than the current `Timeslice` value, then it is removed and re-entered with the current `Timeslice` value plus one, unless this would be equal to or greater than the `end` field of the corresponding `RegionRecord` value. +#### 5. `contribute` -Initially `target` values with only one item MAY be supported. +Regions may be consumed in exchange for a pro rata portion of the Instantaneous Coretime Sales Revenue from its period and regularity. -If the `RegionRecord`'s `price` field is `Some` (indicating that the Region is freshly purchased), then the Broker-chain SHALL record an entry in a storage map called AllowedRenewals. This maps a `CoreIndex` to a struct `RenewalRecord`: +A dispatchable `contribute(region: RegionId, beneficiary: AccountId)` SHALL be provided. -```rust -struct RenewalRecord { - target: Vec<(ParaId, u8)>, - price: Balance, - sale: SaleIndex, -} -``` - -#### 4. Renewals +#### 6. Renewals A dispatchable `renew(core: CoreIndex)` SHALL be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. -This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` containing the same `Vec`, except that: +This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` containing the same `ParaId`, except that: 1. The purchase always succeeds and as such must be processed prior to regular `purchase` orders. 2. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP` of itself or the regular price, whichever is lower. +3. The Region must not have been split. (Transfer and striding are allowed.) +4. The Region must be allocated in exactly the same way as before. -AllowedRenewals map is updated with the new information ready for the following Bulk sale. - -#### 5. Migrations from Legacy Slot Leases +#### 7. Migrations It is intended that paras which hold a lease from the legacy slot auction system are able to seamlessly transition into the Agile Coretime framework. A dispatchable `migrate(core: CoreIndex)` SHALL be provided. Any account may call `migrate` to purchase Bulk Coretime at the current price for the given `core`. -This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` with a value of `Vec` containing just one item equal to `target`, except that: +This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` with a value of `ParaId` containing just one item equal to `target`, except that: 1. The purchase always succeeds and as such MUST be processed prior to regular `purchase` orders. 2. There MUST be an ongoing legacy parachain lease whose parachain is assigned to `core` and which is due to expire during the Coretime period being purchased. @@ -321,18 +266,183 @@ For an efficient market to form around the provision of Bulk-purchased Cores int In order to ensure this, then it is crucial that Instantaneous Coretime, once purchased, cannot be held indefinitely prior to eventual use since, if this were the case, a nefarious collator could purchase Coretime when cheap and utilize it some time later when expensive and deprive private Coretime providers of their revenue. It SHOULD therefore be assumed that Instantaneous Coretime, once purchased, has a definite and short "shelf-life", after which it becomes unusable. This incentivizes collators to avoid purchasing Coretime unless they expect to utilize it imminently and thus helps create an efficient market-feedback mechanism whereby a higher price will actually result in material revenues for private Coretime providers who contribute to the pool of Cores available to service Instantaneous Coretime purchases. -### The Relay-chain API +### Notes on Types -The Relay-chain provides a pallet-based API for the Broker-chain to utilize to inform it in real-time of allocation information for the cores and also to alter the number of cores in the next session. There are two functions which it should expose: +#### Regions -- `fn set_core_count(count: u16)`: Sets the number of active cores from the next session onwards, identified from index `0` to index `count - 1` inclusive. -- `fn assign_core(core_index: u16, assignment: Option>, begin: BlockNumber, end_hint: Option)`: Requests the Relay-chain to provision core identified by `core_index` to process paras identified within the `assignment` vector relatively biased according to the accompanying `u16` *weight* parameter. The weight parameter indicates the ratio of blocks which *on average* should be being processed for the the given para compared to the other paras mentioned in the `assignment` vector. +```rust +enum RecordState { + Fresh { owner: AccountId }, + Instantaneous { beneficiary: AccountId }, + Assigned { target: ParaId }, +} +type Timeslice = u32; // 80 block amounts. +type CoreIndex = u16; +type CorePart = [u8; 10]; // 80-bit bitmap. +struct RegionId { + begin: Timeslice, + core: CoreIndex, + part: CorePart, +} +enum RegionRecord { + Split { one: CorePart }, + Merged { other: CorePart }, + State { + end: Timeslice, + state: RecordState, + }, +} +type Regions = Map; -The Relay-chain should publish, through a well-known storage key, the number of blocks in advance which `assign_core` should be called to ensure that the core gets scheduled properly. We can denote this quantity `ADVANCE_NOTICE`. The Relay-chain MUST respect the core assignment provided that `assign_core` gets processed on a block whose number is no greater than `begin - ADVANCE_NOTICE`. +struct SplitRegion { + core: CoreIndex, + one: CorePart, + other: CorePart, +} +// Processed in forward order. +const MAX_SPLITS_PER_TIMESLICE; +type RegionSplits = Map> +// Processed in reverse order. +const MAX_MERGES_PER_TIMESLICE; +type RegionMerges = Map> -## Implementation +// Tracks the parts that a core has been split into and their next Region; informed by RegionSplits +// and RegionMerges, and used to determine the keys to look up in `Regions`. Initialized to +// `vec![([0xffu8; 10], begin)]` where `begin` is the `begin` field of the `RegionId` key for the +// core's initial region. +type CoreParts = Map>; +``` -Implementation of this proposal comes in several phases: +Example: + +```rust +// Simple example with a `u16` `CorePart` and bulk sold in 100 timeslices. +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Fresh(Alice) }; +// First split @ 50 +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 150u32, state: Fresh(Alice) }; +RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Fresh(Alice) }; +// Share half of first 50 blocks +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => + RegionRecord { end: 150u32, state: Fresh(Alice) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1111_1111u16 => + RegionRecord { end: 150u32, state: Fresh(Alice) }; +RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Fresh(Alice) }; +RegionSplits: 100 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +RegionMerges: 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +// Sell half of them to Bob +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => + RegionRecord { end: 150u32, state: Fresh(Alice) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1111_1111u16 => + RegionRecord { end: 150u32, state: Fresh(Bob) }; +RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Fresh(Alice) }; +RegionSplits: 100 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +RegionMerges: 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +// Bob splits first 10 and assigns them to himself. +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => + RegionRecord { end: 150u32, state: Fresh(Alice) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1111_1111u16 => + RegionRecord { end: 110u32, state: Fresh(Bob) }; +RegionId { core: 0u16, begin: 110 } => 0b0000_0000_1111_1111u16 => + RegionRecord { end: 150u32, state: Fresh(Bob) }; +RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Fresh(Alice) }; +RegionSplits: 100 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +RegionMerges: 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +// Bob shares first 10 3 ways and sells smaller shares to Charlie and Dave +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => + RegionRecord { end: 150u32, state: Fresh(Alice) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1100_0000u16 => + RegionRecord { end: 110u32, state: Fresh(Charlie) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0011_0000u16 => + RegionRecord { end: 110u32, state: Fresh(Dave) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0000_1111u16 => + RegionRecord { end: 110u32, state: Fresh(Bob) }; +RegionId { core: 0u16, begin: 110 } => 0b0000_0000_1111_1111u16 => + RegionRecord { end: 150u32, state: Fresh(Bob) }; +RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Fresh(Alice) }; +RegionSplits: 100 => vec![ + { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } + { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } + { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } +] +RegionMerges: + 110 => vec![ + { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } + { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } + ], + 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +// Bob assigns to his para B, Charlie and Dave assign to their paras C and D; Alice assigns to A +RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => + RegionRecord { end: 150u32, state: Assigned(A) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1100_0000u16 => + RegionRecord { end: 110u32, state: Assigned(C) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0011_0000u16 => + RegionRecord { end: 110u32, state: Assigned(D) }; +RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0000_1111u16 => + RegionRecord { end: 110u32, state: Assigned(B) }; +RegionId { core: 0u16, begin: 110 } => 0b0000_0000_1111_1111u16 => + RegionRecord { end: 150u32, state: Assigned(B) }; +RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => + RegionRecord { end: 200u32, state: Assigned(A) }; +RegionSplits: 100 => vec![ + { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } + { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } + { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } +] +RegionMerges: + 110 => vec![ + { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } + { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } + ], + 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +// Actual notifications to relay chain. +// Assumes: +// - Timeslice is 10 blocks. +// - Timeslice 0 begins at block #1000. +// - Relay needs 10 blocks notice of change. +// +// Block 990: +assign_core(core: 0u16, begin: 1000, assignment: vec![(A, 8), (C, 2), (D, 2), (B, 4)]) +// Block 1090: +assign_core(core: 0u16, begin: 1100, assignment: vec![(A, 8), (B, 8)]) +// Block 1490: +assign_core(core: 0u16, begin: 1500, assignment: vec![(A, 16)]) +``` + +This map functions essentially as a linked list, with one region's `end` field acting as a reference to the next region's key's `begin` field. + +`CoreParts` tracks what parts each core is currently been split into. This must be kept up to date by ensuring that for every new Timeslice, `RegionMerges` is first applied to it in reverse order and then `RegionSplits` applied in regular order. Once done, `CoreParts` can be iterated through to both determine the keys to look up in `Regions` and check at which Timeslice there will be an entry in `Regions`. When a `Region` has been processed, `CoreParts` should also be updated so that the `Timeslice` for the Region's `CorePart` value is set to the `Region`'s `end` field. + +The Broker-chain provides feedback to the Relay-chain on which `ParaId` sets should be serviced on which cores, and does so as they change. Each block, the Broker-chain checks if the period of a `Timeslice` has elapsed. If so, it checks to see if any cores have a newly active `RegionRecord` value in the `Regions` map. If they do, it MUST notify the Relay-chain of the new responsibilities of the relevant `core`. In this case, it MUST remove the item from the `Regions` map and update the `NextRegion` map so it maps the relevant core to the value of removed record's `end` field. + +#### Renewals + +When assigning a Region which is the full `BULK_PERIOD` in span, the `AllowedRenewals` map is altered. + +The map item for the core being assigned to is (re-)initialized if a value does not yet exist or one does exist whose `sale_begin` is less than `begin` value of the `Region` being assigned. Initializing simply means creating a new value with an empty `weighted_targets`, `price` equal to the last Sale Price and `sale_begin` equal to the `begin` value of the `Region` being assigned. + +It is then mutated; `weighted_targets` is changed to include the `target` of the assignment, along with the `u8` weight component equal to the number of bits set in its `CoreParts` bitmap. + +```rust +struct RenewalRecord { + weighted_targets: Vec<(ParaId, u8)>, + price: Balance, + sale_begin: Timeslice, +} +type AllowedRenewals = Map; +``` + +When renewing, `AllowedRenewals` must contain a record for the renewed core whose `sale_begin` is `BULK_PERIOD` blocks less than the period whose sale is approaching and the sum of the second components of `weighted_targets` is 80. If successful, then `price` is increased by itself multiplied by `RENEWAL_PRICE_CAP` and used as a cap to the approaching sale's Sale Price. `weighted_targets` is the assignment of the renewed core. + +### Rollout + +Rollout of this proposal comes in several phases: 1. Finalise the specifics of implementation; this may be done through a design document or through a well-documented prototype implementation. 2. Implement the design, including all associated aspects such as unit tests, benchmarks and any support software needed. From e6654fde4380a6749a1ccf0ba868b00131ea41fc Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 7 Jul 2023 19:25:52 +0200 Subject: [PATCH 14/44] Rename --- RFC-0001-Agile Coretime.md => text/0001-agile-coretime.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename RFC-0001-Agile Coretime.md => text/0001-agile-coretime.md (100%) diff --git a/RFC-0001-Agile Coretime.md b/text/0001-agile-coretime.md similarity index 100% rename from RFC-0001-Agile Coretime.md rename to text/0001-agile-coretime.md From 71c5c47cd39834010888c822d2b73c251d68532c Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 7 Jul 2023 19:31:02 +0200 Subject: [PATCH 15/44] Typo --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index eb21560..41987e4 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -473,7 +473,7 @@ The proposal introduces no new privacy concerns. ## Future Directions and Related Material -RFC-2 proposes a means of implementing the high-level allocations within the Relay-chain. +RFC-3 proposes a means of implementing the high-level allocations within the Relay-chain. Additional work should specify the interface for the instantaneous market revenue so that the Broker chain can ensure Bulk Coretime placed in the instantaneous market is properly compensated. From 591b62ad0d9d24d8c5168cf15e29d106d4cc3797 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 8 Jul 2023 16:10:39 +0200 Subject: [PATCH 16/44] Much more efficient schema --- text/0001-agile-coretime.md | 290 +++++++++++++++++++++--------------- 1 file changed, 173 insertions(+), 117 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 41987e4..b9ed635 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -33,10 +33,11 @@ However, quite possibly the most substantial problem is both a perceived and oft 1. The solution SHOULD provide an acceptable value-capture mechanism for the Polkadot network. 1. The solution SHOULD allow parachains and other projects deployed on to the Polkadot UC to make long-term capital expenditure predictions for the cost of ongoing deployment. -1. The solution SHOULD minimize the barriers to entry in the ecosystem. -1. The solution SHOULD work when the number of cores which the Polkadot UC can support changes over time. -1. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC, including by facilitating the trade of regular core assignment at various intervals and for various spans. -1. The solution SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. +2. The solution SHOULD minimize the barriers to entry in the ecosystem. +3. The solution SHOULD work well when the Polkadot UC has up to 1,000 cores. +4. The solution SHOULD work when the number of cores which the Polkadot UC can support changes over time. +5. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC, including by facilitating the trade of regular core assignment at various intervals and for various spans. +6. The solution SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC should not be unreasonable. @@ -270,157 +271,212 @@ In order to ensure this, then it is crucial that Instantaneous Coretime, once pu #### Regions +This data schema achieves a number of goals: +- Coretime can be individually traded at a level of a single usage of a single core. +- Coretime Regions, of arbitrary span and up to 1/80th interlacing can be exposed as NFTs and exchanged. +- Any Coretime Region can be contributed to the Instantaneous Coretime Pool. +- Unlimited number of individual Coretime contributors to the Instantaneous Coretime Pool. (Effectively limited only in number of cores and interlacing level; with current values this would allow 80,000 individual payees per timeslice). +- All keys are self-describing. +- Workload to communicate core (re-)assignments is well-bounded and low in weight. +- All mandatory bookkeeping workload is well-bounded in weight. + ```rust -enum RecordState { - Fresh { owner: AccountId }, - Instantaneous { beneficiary: AccountId }, - Assigned { target: ParaId }, -} type Timeslice = u32; // 80 block amounts. type CoreIndex = u16; type CorePart = [u8; 10]; // 80-bit bitmap. + +// 128-bit (16 bytes) struct RegionId { begin: Timeslice, core: CoreIndex, part: CorePart, } -enum RegionRecord { - Split { one: CorePart }, - Merged { other: CorePart }, - State { - end: Timeslice, - state: RecordState, - }, +// 296-bit (37 bytes) +struct RegionRecord { + end: Timeslice, + owner: AccountId, } -type Regions = Map; -struct SplitRegion { +map Regions = Map; + +// 40-bit (5 bytes). Could be 32-bit with a more specialised type. +enum CoreTask { + Off, + Assigned { target: ParaId }, + InstaPool, +} +// 113-bit (14 bytes). Could be 14 bytes with a specialised 32-bit `CoreTask`. +struct ScheduleItem { + part: CorePart, // 80 bit + task: CoreTask, // 33 bit +} + +/// The work we plan on having each core do at a particular time in the future. +type Workplan = Map<(Timeslice, CoreIndex), BoundedVec>; +/// The current workload of each core. This gets updated with workplan as timeslices pass. +type Workload = Map>; + +enum Contributor { + System, + Private(AccountId), +} + +struct ContributionRecord { + begin: Timeslice, + end: Timeslice, core: CoreIndex, - one: CorePart, - other: CorePart, + parts: CoreParts, + payee: Contributor, } -// Processed in forward order. -const MAX_SPLITS_PER_TIMESLICE; -type RegionSplits = Map> -// Processed in reverse order. -const MAX_MERGES_PER_TIMESLICE; -type RegionMerges = Map> +type InstaPoolContribution = Map; -// Tracks the parts that a core has been split into and their next Region; informed by RegionSplits -// and RegionMerges, and used to determine the keys to look up in `Regions`. Initialized to -// `vec![([0xffu8; 10], begin)]` where `begin` is the `begin` field of the `RegionId` key for the -// core's initial region. -type CoreParts = Map>; +type SignedTotalParts = u32; +type InstaPoolIo = Map; + +type PoolSize = Value; + +/// Counter for the total CoreParts which could be dedicated to a pool. `u32` so we don't ever get +/// an overflow. +type TotalParts = u32; +struct InstaPoolHistoryRecord { + total_contributions: TotalParts, + maybe_payout: Option, +} +/// Total InstaPool rewards for each Timeslice and the number of core parts which contributed. +type InstaPoolHistory = Map; ``` +`CoreParts` tracks what unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each compnent of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. + +Regions are issued into the `Regions` map and can be transferred, partitioned and interlaced as the owner desires. Regions can only be tasked if they begin after the current scheduling deadline (if they have missed this, then the region can be auto-trimmed until it is). + +Once tasked, they are removed from there and a record is placed in `Workplan`. In addition, if they are contributed to the Instantaneous Coretime Pool, then an entry is placing in `InstaPoolContribution` and `InstaPoolIo`. + +Each timeslice, `InstaPoolIo` is used to update the current value of `PoolSize`. A new entry in `InstaPoolHistory` is inserted, with the `total_contributions` field of `InstaPoolHistoryRecord` being informed by the `PoolSize` value. Each core's has its `Workload` mutated according to its `Workplan` for the upcoming timeslice. + +When Instantaneous Coretime Market Revenues are reported for a particular timeslice from the Relay-chain, this information gets placed in the `maybe_payout` field of the relevant record of `InstaPoolHistory`. + +Payments can be requested made for for any records in `InstaPoolContribution` whose `begin` is the key for a value in `InstaPoolHistory` whose `maybe_payout` is `Some`. In this case, the `total_contributions` is reduced by the `ContributionRecord`'s `parts` and a pro rata amount paid. The `ContributionRecord` is mutated by incrementing `begin`, or removed if `begin` becomes equal to `end`. + Example: ```rust // Simple example with a `u16` `CorePart` and bulk sold in 100 timeslices. -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Fresh(Alice) }; +Regions: +{ core: 0u16, begin: 100, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // First split @ 50 -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 150u32, state: Fresh(Alice) }; -RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Fresh(Alice) }; +Regions: +{ core: 0u16, begin: 100, part: 0b1111_1111_1111_1111u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Share half of first 50 blocks -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => - RegionRecord { end: 150u32, state: Fresh(Alice) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1111_1111u16 => - RegionRecord { end: 150u32, state: Fresh(Alice) }; -RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Fresh(Alice) }; -RegionSplits: 100 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] -RegionMerges: 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +Regions: +{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Sell half of them to Bob -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => - RegionRecord { end: 150u32, state: Fresh(Alice) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1111_1111u16 => - RegionRecord { end: 150u32, state: Fresh(Bob) }; -RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Fresh(Alice) }; -RegionSplits: 100 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] -RegionMerges: 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +Regions: +{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; +{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Bob splits first 10 and assigns them to himself. -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => - RegionRecord { end: 150u32, state: Fresh(Alice) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1111_1111u16 => - RegionRecord { end: 110u32, state: Fresh(Bob) }; -RegionId { core: 0u16, begin: 110 } => 0b0000_0000_1111_1111u16 => - RegionRecord { end: 150u32, state: Fresh(Bob) }; -RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Fresh(Alice) }; -RegionSplits: 100 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] -RegionMerges: 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +Regions: +{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, part: 0b0000_0000_1111_1111u16 } => { end: 110u32, owner: Bob }; +{ core: 0u16, begin: 110, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; +{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Bob shares first 10 3 ways and sells smaller shares to Charlie and Dave -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => - RegionRecord { end: 150u32, state: Fresh(Alice) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1100_0000u16 => - RegionRecord { end: 110u32, state: Fresh(Charlie) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0011_0000u16 => - RegionRecord { end: 110u32, state: Fresh(Dave) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0000_1111u16 => - RegionRecord { end: 110u32, state: Fresh(Bob) }; -RegionId { core: 0u16, begin: 110 } => 0b0000_0000_1111_1111u16 => - RegionRecord { end: 150u32, state: Fresh(Bob) }; -RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Fresh(Alice) }; -RegionSplits: 100 => vec![ - { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } - { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } - { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } +Regions: +{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, part: 0b0000_0000_1100_0000u16 } => { end: 110u32, owner: Charlie }; +{ core: 0u16, begin: 100, part: 0b0000_0000_0011_0000u16 } => { end: 110u32, owner: Dave }; +{ core: 0u16, begin: 100, part: 0b0000_0000_0000_1111u16 } => { end: 110u32, owner: Bob }; +{ core: 0u16, begin: 110, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; +{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +// Bob assigns to his para B, Charlie and Dave assign to their paras C and D; Alice assigns first 50 to A +Regions: +{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +Workplan: +(100, 0) => vec![ + { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { part: 0b0000_0000_1100_0000u16, task: Assigned(C) }, + { part: 0b0000_0000_0011_0000u16, task: Assigned(D) }, + { part: 0b0000_0000_0000_1111u16, task: Assigned(B) }, ] -RegionMerges: - 110 => vec![ - { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } - { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } - ], - 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] -// Bob assigns to his para B, Charlie and Dave assign to their paras C and D; Alice assigns to A -RegionId { core: 0u16, begin: 100 } => 0b1111_1111_0000_0000u16 => - RegionRecord { end: 150u32, state: Assigned(A) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_1100_0000u16 => - RegionRecord { end: 110u32, state: Assigned(C) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0011_0000u16 => - RegionRecord { end: 110u32, state: Assigned(D) }; -RegionId { core: 0u16, begin: 100 } => 0b0000_0000_0000_1111u16 => - RegionRecord { end: 110u32, state: Assigned(B) }; -RegionId { core: 0u16, begin: 110 } => 0b0000_0000_1111_1111u16 => - RegionRecord { end: 150u32, state: Assigned(B) }; -RegionId { core: 0u16, begin: 150 } => 0b1111_1111_1111_1111u16 => - RegionRecord { end: 200u32, state: Assigned(A) }; -RegionSplits: 100 => vec![ - { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } - { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } - { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } +(110, 0) => vec![{ part: 0b0000_0000_1111_1111u16, task: Assigned(B) }] +// Alice assigns her remaining 50 timeslices to the InstaPool paying herself: +Regions: (empty) +Workplan: +(100, 0) => vec![ + { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { part: 0b0000_0000_1100_0000u16, task: Assigned(C) }, + { part: 0b0000_0000_0011_0000u16, task: Assigned(D) }, + { part: 0b0000_0000_0000_1111u16, task: Assigned(B) }, ] -RegionMerges: - 110 => vec![ - { core: 0, one: 0b0000_0000_1111_0000u16, other: 0b0000_0000_0000_1111u16 } - { core: 0, one: 0b0000_0000_0000_1100u16, other: 0b0000_0000_0000_0011u16 } - ], - 150 => vec![ { core: 0, one: 0b1111_1111_0000_0000u16, other: 0b0000_0000_1111_1111u16 } ] +(110, 0) => vec![{ part: 0b0000_0000_1111_1111u16, task: Assigned(B) }] +(150, 0) => vec![{ part: 0b1111_1111_1111_1111u16, task: InstaPool }] +InstaPoolContribution: +{ begin: 150, end: 200, core: 0, parts: 0b1111_1111_1111_1111u16, payee: Alice } +InstaPoolIo: +150 => 16 +200 => -16 // Actual notifications to relay chain. // Assumes: // - Timeslice is 10 blocks. // - Timeslice 0 begins at block #1000. // - Relay needs 10 blocks notice of change. // +Workload: 0 => vec![] +PoolSize: 0 + // Block 990: -assign_core(core: 0u16, begin: 1000, assignment: vec![(A, 8), (C, 2), (D, 2), (B, 4)]) +Relay <= assign_core(core: 0u16, begin: 1000, assignment: vec![(A, 8), (C, 2), (D, 2), (B, 4)]) +Workload: 0 => vec![ + { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { part: 0b0000_0000_1100_0000u16, task: Assigned(C) }, + { part: 0b0000_0000_0011_0000u16, task: Assigned(D) }, + { part: 0b0000_0000_0000_1111u16, task: Assigned(B) }, +] +PoolSize: 0 + // Block 1090: -assign_core(core: 0u16, begin: 1100, assignment: vec![(A, 8), (B, 8)]) +Relay <= assign_core(core: 0u16, begin: 1100, assignment: vec![(A, 8), (B, 8)]) +Workload: 0 => vec![ + { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { part: 0b0000_0000_1111_1111u16, task: Assigned(B) }, +] +PoolSize: 0 + // Block 1490: -assign_core(core: 0u16, begin: 1500, assignment: vec![(A, 16)]) +Relay <= assign_core(core: 0u16, begin: 1500, assignment: vec![(Pool, 16)]) +Workload: 0 => vec![ + { part: 0b1111_1111_1111_1111u16, task: InstaPool }, +] +PoolSize: 16 +InstaPoolIo: +200 => -16 +InstaPoolHistory: +150 => { total_contributions: 16, maybe_payout: None } + +// Sometime after block 1500: +InstaPoolHistory: +150 => { total_contributions: 16, maybe_payout: Some(P) } + +// Sometime after block 1990: +InstaPoolIo: (empty) +PoolSize: 0 +InstaPoolHistory: +150 => { total_contributions: 16, maybe_payout: Some(P0) } +151 => { total_contributions: 16, maybe_payout: Some(P1) } +152 => { total_contributions: 16, maybe_payout: Some(P2) } +... +199 => { total_contributions: 16, maybe_payout: Some(P49) } + +// Sometime later still Alice calls for a payout +InstaPoolContribution: (empty) +InstaPoolHistory: (empty) +// Alice gets rewarded P0 + P1 + ... P49. ``` -This map functions essentially as a linked list, with one region's `end` field acting as a reference to the next region's key's `begin` field. - -`CoreParts` tracks what parts each core is currently been split into. This must be kept up to date by ensuring that for every new Timeslice, `RegionMerges` is first applied to it in reverse order and then `RegionSplits` applied in regular order. Once done, `CoreParts` can be iterated through to both determine the keys to look up in `Regions` and check at which Timeslice there will be an entry in `Regions`. When a `Region` has been processed, `CoreParts` should also be updated so that the `Timeslice` for the Region's `CorePart` value is set to the `Region`'s `end` field. - -The Broker-chain provides feedback to the Relay-chain on which `ParaId` sets should be serviced on which cores, and does so as they change. Each block, the Broker-chain checks if the period of a `Timeslice` has elapsed. If so, it checks to see if any cores have a newly active `RegionRecord` value in the `Regions` map. If they do, it MUST notify the Relay-chain of the new responsibilities of the relevant `core`. In this case, it MUST remove the item from the `Regions` map and update the `NextRegion` map so it maps the relevant core to the value of removed record's `end` field. - #### Renewals When assigning a Region which is the full `BULK_PERIOD` in span, the `AllowedRenewals` map is altered. From 562ef32e484ed23216e5cf71da569e59740d8822 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 8 Jul 2023 16:11:34 +0200 Subject: [PATCH 17/44] mention RFC-5 --- text/0001-agile-coretime.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index b9ed635..6dcb1fe 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -531,6 +531,8 @@ The proposal introduces no new privacy concerns. RFC-3 proposes a means of implementing the high-level allocations within the Relay-chain. +RFC-5 proposes the API for interacting with Relay-chain. + Additional work should specify the interface for the instantaneous market revenue so that the Broker chain can ensure Bulk Coretime placed in the instantaneous market is properly compensated. ## Drawbacks, Alternatives and Unknowns From 642782d443b658fe7925e5262353d53d92341047 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 9 Jul 2023 08:35:12 +0200 Subject: [PATCH 18/44] Details --- text/0001-agile-coretime.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 6dcb1fe..64221f8 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -180,16 +180,15 @@ An implementation of the `nonfungible` trait SHOULD include equivalent functiona Regions may be consumed in exchange for being assigned to a core. -A dispatchable `assign(region: RegionId, target: ParaId)` SHALL be provided corresponding to the `allocate` function described above. +A `assign(region: RegionId, target: ParaId)` dispatchable SHALL have the effect of removing the region identified by `region` and placing an item in the workplan corresponding to the region's properties and assigned to the `target` task. -It MUST be called with a Signed origin equal to the `owner` field of the value of the Regions map for the `region` key. The `allocation` field of this value MUST be `None` (a region may not be re-allocated). - -On success, the `assign` value is changed to `Some` whose inner value is equal to `target`. - -If the `begin` field of the `region` parameter is less than the current `Timeslice` value, then it is removed and re-entered with the current `Timeslice` value plus one, unless this would be equal to or greater than the `end` field of the corresponding `RegionRecord` value. +If the region's end has already passed (taking into account any advance notice requirements) then this operation is a no-op. If the region's begining has already passed, then it is effectively altered to become the next schedulable timeslice. If the Region's span is the entire `BULK_PERIOD`, then the Broker-chain records in storage that the allocation happened during this period in order to facilitate the possibility for a renewal. +Also: +- `owner` field of `region` must the equal to the Signed origin. + #### 3. `partition` Regions may be split apart into two non-overlapping interior Regions of the same regularity. @@ -210,11 +209,16 @@ Also: - `owner` field of `region` must the equal to the Signed origin. - `parts` must have some bits set AND must not equal the Core Parts of the old Region AND must only have bits set which are also set in the old Region's' Core Parts. -#### 5. `contribute` +#### 5. `pool` Regions may be consumed in exchange for a pro rata portion of the Instantaneous Coretime Sales Revenue from its period and regularity. -A dispatchable `contribute(region: RegionId, beneficiary: AccountId)` SHALL be provided. +A `pool(region: RegionId, beneficiary: AccountId)` dispatchable SHALL have the effect of removing the region identified by `region` and placing an item in the workplan corresponding to the region's properties and assigned to the Instantaneous Coretime Pool. The details of the region will be recorded in order to allow the proper allocation of Instantaneous Coretime Sales Revenue. + +If the region's end has already passed (taking into account any advance notice requirements) then this operation is a no-op. If the region's begining has already passed, then it is effectively altered to become the next schedulable timeslice. + +Also: +- `owner` field of `region` must the equal to the Signed origin. #### 6. Renewals From cbbf41e1a5f52338f3eed5c5a8d6688bc9e5b556 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 9 Jul 2023 08:37:46 +0200 Subject: [PATCH 19/44] typo --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 64221f8..1736483 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -15,7 +15,7 @@ This proposes a periodic, sale-based method for assigning Polkadot Coretime. The ### Present System -The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 18 months in advance. Practically speaking, we only see two year periods being bid upon and leased. +The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 24 months in advance. Practically speaking, we only see two year periods being bid upon and leased. Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expiry of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. From 3117eb2b959fd045277cb7c6d1ba4fbfff8e4fe2 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 9 Jul 2023 08:42:30 +0200 Subject: [PATCH 20/44] Intro PUC --- text/0001-agile-coretime.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 1736483..2c2428c 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -15,7 +15,9 @@ This proposes a periodic, sale-based method for assigning Polkadot Coretime. The ### Present System -The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer (aka "Polkadot") is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 24 months in advance. Practically speaking, we only see two year periods being bid upon and leased. +The *Polkadot Ubiquitous Computer*, or just *Polkadot UC*, represents the public service provided by the Polkadot Network. It is a trust-free, WebAssembly-based, multicore, internet-native omnipresent virtual machine highly resilient to interfence and corruption. + +The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 24 months in advance. Practically speaking, we only see two year periods being bid upon and leased. Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expiry of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. From 9a9754661486b307e92addb70ec90847fbb2fe2a Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 9 Jul 2023 08:47:12 +0200 Subject: [PATCH 21/44] Bulktime definitions --- text/0001-agile-coretime.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 2c2428c..9bffda1 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -134,12 +134,10 @@ A sale of Bulk Coretime occurs on the Broker Chain every `BULK_PERIOD` blocks. In every sale, a `BULK_LIMIT` of individual *Regions* are offered for sale at a particular Sale Price. -The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin `LEADIN_PERIOD` blocks into the future at the time of the sale. - -The Regions offered for sale also have complete - Each Region offered for sale has a different Core Index, ensuring that they each represent an independently allocatable resource on the Polkadot UC. +The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin `LEADIN_PERIOD` blocks into the future at the time of the sale. The Regions offered for sale also have the complete, non-interlaced, Core. + After every sale, the Next Sale Price is set according to the Previous Sale Price and the number of Regions sold compared to the desired and maximum amount of Regions to be sold. See Price Setting for additional detail. This is designed to give a minimum of around two weeks worth of time for it to be sliced, shared, traded and allocated. From d4cdf6919c7cef8b4b187726019063ebea2aec78 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 9 Jul 2023 08:51:53 +0200 Subject: [PATCH 22/44] Correct example price setting algo --- text/0001-agile-coretime.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 9bffda1..72815d8 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -253,10 +253,10 @@ In general we would expect the price to increase the closer `CORES_SOLD` gets to A simple example of this would be the formula: ``` -NEW_PRICE := IF CORES_OLD < BULK_TARGET THEN - OLD_PRICE - OLD_PRICE / 2 * CORES_SOLD / BULK_TARGET +NEW_PRICE := IF CORES_SOLD < BULK_TARGET THEN + OLD_PRICE * MAX(CORES_SOLD, 1) / BULK_TARGET ELSE - OLD_PRICE + OLD_PRICE / 2 * + OLD_PRICE + OLD_PRICE * (CORES_SOLD - BULK_TARGET) / (BULK_LIMIT - BULK_TARGET) END IF ``` From bc4235c7e6f12258524c7caa973dd19795f8d048 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 10 Jul 2023 12:23:20 +0200 Subject: [PATCH 23/44] Update sale period/renewas --- text/0001-agile-coretime.md | 59 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 72815d8..41a061a 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -104,7 +104,9 @@ This proposal includes a number of parameters which need not necessarily be fixe | Name | Value | | | ------------------- | ---------------------------- | ---------- | | `BULK_PERIOD` | `28 * DAYS` | specified | -| `LEADIN_PERIOD` | `14 * DAYS` | specified | +| `INTERLUDE_PERIOD` | `7 * DAYS` | specified | +| `LEADIN_PERIOD` | `7 * DAYS` | specified | +| `STEP_PERIOD` | `8 * HOURS` | specified | | `TIMESLICE` | `8 * MINUTES` | specified | | `BULK_TARGET` | `30` | suggested | | `BULK_LIMIT` | `45` | suggested | @@ -136,21 +138,31 @@ In every sale, a `BULK_LIMIT` of individual *Regions* are offered for sale at a Each Region offered for sale has a different Core Index, ensuring that they each represent an independently allocatable resource on the Polkadot UC. -The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin `LEADIN_PERIOD` blocks into the future at the time of the sale. The Regions offered for sale also have the complete, non-interlaced, Core. +The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin immediately following the span of the previous Sale's Regions. The Regions offered for sale also have the complete, non-interlaced, Core. -After every sale, the Next Sale Price is set according to the Previous Sale Price and the number of Regions sold compared to the desired and maximum amount of Regions to be sold. See Price Setting for additional detail. +The sale period ends immediately as soon as span of the Coretime Regions that are being sold begins. At this point, the next Sale Price is set according to the previous Sale Price and the number of Regions sold compared to the desired and maximum amount of Regions to be sold. See Price Setting for additional detail. -This is designed to give a minimum of around two weeks worth of time for it to be sliced, shared, traded and allocated. +Following this, there is an *Interlude* period lasting `INTERLUDE_PERIOD` of blocks before the next Sale becomes active. After this period is elapsed, *Regular Purchasing* may begin (see next). -The Broker-chain aims to sell some `BULK_TARGET` month-long Regions, up to some maximum amount of `BULK_LIMIT`. It makes this sale in a single batch prior to the beginning of the period being sold by `LEADIN_PERIOD` blocks. The Broker-chain publishes the price for a Region prior to the sale execution. +This is designed to give at least two weeks worth of time for the purchased regions to be partitioned, interlaced, traded and allocated. -#### Purchasing +#### Regular Purchasing -Accounts may call a `purchase` function with the appropriate funds in place to have their funds placed On Hold and signal an commitment to purchase Bulk Coretime for the forthcoming period. One account may have only one pending purchase. Any number of accounts may attempt to `purchase` Bulk Coretime for the forthcoming period and this order is recorded. +Any account may purchase Regions of Bulk Coretime if they have the appropriate funds in place during the *Purchasing Period*, which is from `INTERLUDE_PERIOD` blocks after the end of the previous sale until the beginning of the Region of the Bulk Coretime which is for sale as long as there are Regions of Bulk Coretime left for sale (i.e. no more than `BULK_LIMIT` have already been sold in the Bulk Coretime Sale). -Requests are processed in incoming order. If there are more purchase requests than available Regions for purchase then remaining purchase orders are carried over to the next sale and flagged as carried. A purchase is only cancellable (and the funds On Hold refundable) if it is so flagged. +The Purchasing Period is roughly `BULK_PERIOD - INTERLUDE_PERIOD` blocks in length. The price various during an initial portion of the Purchasing Period and then stays stable for the remainder. This initial portion is `LEADIN_PERIOD` blocks in length. During this period the price decreases monotonically from the Sale Price multiplied by `LEADIN_MULTIPLIER` to the basic Sale Price over a number of discrete drops, each `STEP_PERIOD` blocks in length. -When a purchase request is processed, the Funds On Hold are transferred to the local Treasury and a fresh Region of the proper span is issued and assigned to the purchaser as the owner. +The eventual usage of the funds paid is out of scope for the present proposal. + +#### Renewals + +At any time when there are there are remaining Regions of Bulk Coretime to be sold, *including during the Interlude Period*, then certain Bulk Coretime assignmnents may be *Renewed*. This is similar to a purchase in that funds must be paid and it consumes one of the Regions of Bulk Coretime which would otherwise be placed for purchase. However there are two key differences. + +Firstly, the price paid is exactly `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous sale. + +Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. + +Renewal is only possible for either cores which have been assigned as a result of a previous renewal, or which begin their Bulk Coretime with a fully allocated workload which does not include allocation to the Instantaneous Coretime Pool. In the latter case, the renewed workload will be the same as this initial workload. #### Manipulation @@ -160,7 +172,7 @@ Regions may be manipulated in various ways by its owner: 1. *Assigned* to a single, specific ParaID task. 1. *Partitioned* into quantized, non-overlapping segments of Bulk Coretime with the same ownership. 1. *Interlaced* into multiple Regions over the same period whose eventual assignments take turns to be scheduled. -1. *Contributed* into the Instantaneous Coretime Pool, in return for a pro-rata amount of the revenue from the Instantaneous Coretime Sales over its period. +1. *Pooled* into the Instantaneous Coretime Pool, in return for a pro-rata amount of the revenue from the Instantaneous Coretime Sales over its period. #### Enactment @@ -224,12 +236,10 @@ Also: A dispatchable `renew(core: CoreIndex)` SHALL be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. -This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` containing the same `ParaId`, except that: +This may be called during the Interlude Period as well as the regular Purchasing Period and has the same effect as `purchase` followed by `allocate`, except that: -1. The purchase always succeeds and as such must be processed prior to regular `purchase` orders. -2. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP` of itself or the regular price, whichever is lower. -3. The Region must not have been split. (Transfer and striding are allowed.) -4. The Region must be allocated in exactly the same way as before. +1. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP` of itself or the regular price, whichever is lower. +1. The Region must be allocated as the given `core` was allocated at the beginning of the present Region. #### 7. Migrations @@ -481,25 +491,6 @@ InstaPoolHistory: (empty) // Alice gets rewarded P0 + P1 + ... P49. ``` -#### Renewals - -When assigning a Region which is the full `BULK_PERIOD` in span, the `AllowedRenewals` map is altered. - -The map item for the core being assigned to is (re-)initialized if a value does not yet exist or one does exist whose `sale_begin` is less than `begin` value of the `Region` being assigned. Initializing simply means creating a new value with an empty `weighted_targets`, `price` equal to the last Sale Price and `sale_begin` equal to the `begin` value of the `Region` being assigned. - -It is then mutated; `weighted_targets` is changed to include the `target` of the assignment, along with the `u8` weight component equal to the number of bits set in its `CoreParts` bitmap. - -```rust -struct RenewalRecord { - weighted_targets: Vec<(ParaId, u8)>, - price: Balance, - sale_begin: Timeslice, -} -type AllowedRenewals = Map; -``` - -When renewing, `AllowedRenewals` must contain a record for the renewed core whose `sale_begin` is `BULK_PERIOD` blocks less than the period whose sale is approaching and the sum of the second components of `weighted_targets` is 80. If successful, then `price` is increased by itself multiplied by `RENEWAL_PRICE_CAP` and used as a cap to the approaching sale's Sale Price. `weighted_targets` is the assignment of the renewed core. - ### Rollout Rollout of this proposal comes in several phases: From ececc836abd265e66969702827bcf94d678cadf1 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 10 Jul 2023 17:32:11 +0200 Subject: [PATCH 24/44] Explanation --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 41a061a..a85ea9f 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -122,7 +122,7 @@ A *Region* is an assignable period of Coretime with a known regularity. All Regions are associated with a unique *Core Index*, to identify which core the Region controls the assignment of. -All Regions are also associated with a *Core Parts*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Parts value, it is said to be *Complete*. +All Regions are also associated with a *Core Parts*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Parts value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Part bitmap represents exactly one Core for one Relay-chain block in one Timeslice. All Regions have a span. Region spans are quantized into periods of `TIMESLICE` blocks; `BULK_PERIOD` divides into `TIMESLICE` a whole number of times. From 7c92da08c82f4679df7e48ca4667e21dd4d262cd Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 17 Jul 2023 11:59:39 +0200 Subject: [PATCH 25/44] Update text/0001-agile-coretime.md Co-authored-by: Muharem Ismailov --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index a85ea9f..84eb337 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -156,7 +156,7 @@ The eventual usage of the funds paid is out of scope for the present proposal. #### Renewals -At any time when there are there are remaining Regions of Bulk Coretime to be sold, *including during the Interlude Period*, then certain Bulk Coretime assignmnents may be *Renewed*. This is similar to a purchase in that funds must be paid and it consumes one of the Regions of Bulk Coretime which would otherwise be placed for purchase. However there are two key differences. +At any time when there are remaining Regions of Bulk Coretime to be sold, *including during the Interlude Period*, then certain Bulk Coretime assignmnents may be *Renewed*. This is similar to a purchase in that funds must be paid and it consumes one of the Regions of Bulk Coretime which would otherwise be placed for purchase. However there are two key differences. Firstly, the price paid is exactly `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous sale. From 38a0189132b02a54aff09285de64c8a9d17a524a Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 17 Jul 2023 11:59:57 +0200 Subject: [PATCH 26/44] Update text/0001-agile-coretime.md Co-authored-by: Muharem Ismailov --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 84eb337..27afa0b 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -370,7 +370,7 @@ Each timeslice, `InstaPoolIo` is used to update the current value of `PoolSize`. When Instantaneous Coretime Market Revenues are reported for a particular timeslice from the Relay-chain, this information gets placed in the `maybe_payout` field of the relevant record of `InstaPoolHistory`. -Payments can be requested made for for any records in `InstaPoolContribution` whose `begin` is the key for a value in `InstaPoolHistory` whose `maybe_payout` is `Some`. In this case, the `total_contributions` is reduced by the `ContributionRecord`'s `parts` and a pro rata amount paid. The `ContributionRecord` is mutated by incrementing `begin`, or removed if `begin` becomes equal to `end`. +Payments can be requested made for any records in `InstaPoolContribution` whose `begin` is the key for a value in `InstaPoolHistory` whose `maybe_payout` is `Some`. In this case, the `total_contributions` is reduced by the `ContributionRecord`'s `parts` and a pro rata amount paid. The `ContributionRecord` is mutated by incrementing `begin`, or removed if `begin` becomes equal to `end`. Example: From 6213a2c115488714dfe4a88370f2829fd4900449 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 18 Jul 2023 11:59:39 +0200 Subject: [PATCH 27/44] Update with insights from implementation --- text/0001-agile-coretime.md | 260 +++++++++++++++++++++++------------- 1 file changed, 166 insertions(+), 94 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 27afa0b..b2350a4 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -9,27 +9,27 @@ ## Summary -This proposes a periodic, sale-based method for assigning Polkadot Coretime. The method takes into account the need for long-term capital expenditure planning for teams building on Polkadot, yet also provides a means to allow Polkadot to capture long-term value in the resource which it sells. It supports the possibility of building secondary markets to make resource allocation more efficient and largely avoids the need for parameterisation. +This proposes a periodic, sale-based method for assigning Polkadot Coretime, the analogue of "block space" within the Polkadot Network. The method takes into account the need for long-term capital expenditure planning for teams building on Polkadot, yet also provides a means to allow Polkadot to capture long-term value in the resource which it sells. It supports the possibility of building rich and dynamic secondary markets to optimize resource allocation and largely avoids the need for parameterization. ## Motivation ### Present System -The *Polkadot Ubiquitous Computer*, or just *Polkadot UC*, represents the public service provided by the Polkadot Network. It is a trust-free, WebAssembly-based, multicore, internet-native omnipresent virtual machine highly resilient to interfence and corruption. +The *Polkadot Ubiquitous Computer*, or just *Polkadot UC*, represents the public service provided by the Polkadot Network. It is a trust-free, WebAssembly-based, multicore, internet-native omnipresent virtual machine which is highly resilient to interference and corruption. -The present system of allocating time for parachains on the cores of the Polkadot Ubiquitous Computer is through a process known as *slot auctions*. These are on-chain candle auctions which proceed for several days and result in a core being assigned to a single parachain for six months at a time up to 24 months in advance. Practically speaking, we only see two year periods being bid upon and leased. +The present system of allocating the limited resources of the Polkadot Ubiquitous Computer is through a process known as *parachain slot auctions*. This is a parachain-centric paradigm whereby a single core is long-term allocated to a single parachain which itself implies a Substrate/Cumulus-based chain secured and connected via the Relay-chain. Slot auctions are on-chain candle auctions which proceed for several days and result in the core being assigned to the parachain for six months at a time up to 24 months in advance. Practically speaking, we only see two year periods being bid upon and leased. -Funds behind the bids made in the slot auctions are merely locked, not consumed or paid and become unlocked and returned to the bidder on expiry of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. +Funds behind the bids made in the slot auctions are merely locked, they are not consumed or paid and become unlocked and returned to the bidder on expiry of the lease period. A means of sharing the deposit trustlessly known as a *crowdloan* is available allowing token holders to contribute to the overall deposit of a chain without any counterparty risk. ### Problems -The present system is based on a model of one-core-per-parachain. This is a legacy interpretation of the Polkadot platform and is not a reflection of its present capabilities. By restricting ownership and usage to this model, more dynamic and resource-efficient means of utilising the Polkadot Ubiquitous Computer is lost. +The present system is based on a model of one-core-per-parachain. This is a legacy interpretation of the Polkadot platform and is not a reflection of its present capabilities. By restricting ownership and usage to this model, more dynamic and resource-efficient means of utilizing the Polkadot Ubiquitous Computer are lost. -More specifically, it is impossible to lease out cores at anything less than six months, and apparently unrealistic to do so at anything less than two years. This cuts out the ability to dynamically manage the underlying resource, and generally experimentation, iteration and innovation are hampered. It bakes into the platform an assumption of permanence for anything deployed into it and restricts the market's ability to find a more optimal allocation of the finite resource. +More specifically, it is impossible to lease out cores at anything less than six months, and apparently unrealistic to do so at anything less than two years. This removes the ability to dynamically manage the underlying resource, and generally experimentation, iteration and innovation suffer. It bakes into the platform an assumption of permanence for anything deployed into it and restricts the market's ability to find a more optimal allocation of the finite resource. There is no ability to determine capital requirements for hosting a parachain beyond two years from the point of its initial deployment onto Polkadot. While it would be unreasonable to have perfect and indefinite cost predictions for any real-world platform, not having any clarity whatsoever beyond "market rates" two years hence can be a very off-putting prospect for teams to buy into. -However, quite possibly the most substantial problem is both a perceived and often real high barrier to entry of the Polkadot ecosystem. By forcing innovators to either raise 7-figure sums through investors or appeal to the wider token-holding community, Polkadot makes it essentially difficult for a small band of innovators from deploying their technology into Polkadot. While not being actually permissioned, it is also far from the barrierless, permissionless ideal which an innovation platform such as Polkadot should be striving for. +However, quite possibly the most substantial problem is both a perceived and often real high barrier to entry of the Polkadot ecosystem. By forcing innovators to either raise seven-figure sums through investors or appeal to the wider token-holding community, Polkadot makes it difficult for a small band of innovators to deploy their technology into Polkadot. While not being actually permissioned, it is also far from the barrierless, permissionless ideal which an innovation platform such as Polkadot should be striving for. ## Requirements @@ -38,7 +38,7 @@ However, quite possibly the most substantial problem is both a perceived and oft 2. The solution SHOULD minimize the barriers to entry in the ecosystem. 3. The solution SHOULD work well when the Polkadot UC has up to 1,000 cores. 4. The solution SHOULD work when the number of cores which the Polkadot UC can support changes over time. -5. The solution SHOULD facilitate the optimal allocation of work to core of the Polkadot UC, including by facilitating the trade of regular core assignment at various intervals and for various spans. +5. The solution SHOULD facilitate the optimal allocation of work to cores of the Polkadot UC, including by facilitating the trade of regular core assignment at various intervals and for various spans. 6. The solution SHOULD avoid creating additional dependencies on functionality which the Relay-chain need not strictly provide for the delivery of the Polkadot UC. Furthermore, the design SHOULD be implementable and deployable in a timely fashion; three months from the acceptance of this RFC should not be unreasonable. @@ -53,45 +53,63 @@ Primary stakeholder sets are: _Socialization:_ -This RFC was presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A small amount of socialisation at the Parachain Summit preceeded it and some substantial discussion followed it. Parity Ecosystem team is currently soliciting views from ecosystem teams who would be key stakeholders. +The essensials of this proposal were presented at Polkadot Decoded 2023 Copenhagen on the Main Stage. A small amount of socialization at the Parachain Summit preceeded it and some substantial discussion followed it. Parity Ecosystem team is currently soliciting views from ecosystem teams who would be key stakeholders. ## Explanation ### Overview -Upon implementation of this proposal, slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk* and *Instantaneous*. This proposal only mandates the implementation of *Bulk Coretime*; any mentions of *Instantaneous Coretime* are only intended to illustrate a possible final solution. +Upon implementation of this proposal, the parachain-centric slot auctions and associated crowdloans cease. Instead, Coretime on the Polkadot UC is sold by the Polkadot System in two separate formats: *Bulk Coretime* and *Instantaneous Coretime*. -Bulk Coretime is sold periodically on a specialised *Brokerage System Chain* and allocated in advance of its usage, whereas Instantaneous Coretime is sold on the Relay-chain immediately prior to usage on a block-by-block basis with an explicit allocation at the time of purchase. +When a Polkadot Core is utilized, we say it is dedicated to a *Task* rather than a "parachain". The Task to which a Core is dedicated may change at every Relay-chain block and while one predominant type of Task is to secure a Cumulus-based blockchain (i.e. a parachain), other types of Tasks are envisioned. -Revenue from sales of Bulk Coretime is owned by the Polkadot Treasury. Owners of Bulk Coretime are tracked on this chain and the ownership status and properties of the owned Coretime are exposed over XCM as a non-fungible asset. +Bulk Coretime is sold periodically on a specialised system chain known as the *Coretime-chain* and allocated in advance of its usage, whereas Instantaneous Coretime is sold on the Relay-chain immediately prior to usage on a block-by-block basis. -At the request of the owner, the Broker-chain allows a single Bulk Coretime asset, known as a *Region*, to be used in various ways including transferal to another owner, allocated to a particular parachain or placed in the Instantaneous Coretime Pool. Regions can also be split out, either into non-overlapping sub-spans or exactly-overlapping spans with less regularity. +This proposal does not fix what should be done with revenue from sales of Coretime and leaves it for a further RFC process. -The Brokerage System Chain periodically instructs the Relay-chain to assign its cores to alternative tasks as and when the allocation changes owing to a new Region taking effect. +Owners of Bulk Coretime are tracked on the Coretime-chain and the ownership status and properties of the owned Coretime are exposed over XCM as a non-fungible asset. -#### Special Considerations +At the request of the owner, the Coretime-chain allows a single Bulk Coretime asset, known as a *Region*, to be used in various ways including transferal to another owner, allocated to a particular task (e.g. a parachain) or placed in the Instantaneous Coretime Pool. Regions can also be split out, either into non-overlapping sub-spans or exactly-overlapping spans with less regularity. -As a migration mechanism, pre-existing leases (from the legacy lease/slots/crowdloan framework) SHALL be recorded in the Broker-chain and cores assigned to them. When the lease comes to expiry, there is a mechanism to gain a priority sale of Bulk Coretime to ensure that the Parachain suffers no downtime. +The Coretime-Chain periodically instructs the Relay-chain to assign its cores to alternative tasks as and when Core allocations change due to new Regions coming into effect. -Additionally, there is a renewal system which allows a core's assignment to be renewed unchanged with a known maximum price increase from month to month. In this case, the core's assignment must not include an Instantaneous Coretime allocation and must not have been split into smaller amounts. +#### Renewal and Migration -Note that the intention behind renewals is only to ensure that committed Paras get some guarantees about price for predicting future costs. As such this price-capped renewal system only allows cores to be reused for their same tasks from month to month. In any other context, the regular sale system must be used and the regular price paid. +There is a renewal system which allows a Bulk Coretime assignment of a single core to be renewed unchanged with a known price increase from month to month. Renewals are processed in a period prior to regular purchases, effectively giving them precedence over a fixed number of cores available. -#### Relay-chain and Instantaneous Coretime +Renewals are only enabled when a core's assignment does not include an Instantaneous Coretime allocation and has not been split into shorter segments. -Sales of Instantaneous Coretime happen on the Polkadot Relay-chain. The Relay-chain is expected to be responsible for: +Thus, renewals are designed to ensure only that committed parachains get some guarantees about price for predicting future costs. This price-capped renewal system only allows cores to be reused for their same tasks from month to month. In any other context, Bulk Coretime would need to be purchased regularly. -- holding non-transferable, non-refundable DOT-denominated Instantaneous Coretime Market Credit balance information for collators. +As a migration mechanism, pre-existing leases (from the legacy lease/slots/crowdloan framework) are initialized into the Coretime-chain and cores assigned to them prior to Bulk Coretime sales. In the sale where the lease expires, the system offers a renewal, as above, to allow a priority sale of Bulk Coretime and ensure that the Parachain suffers no downtime when transitioning from the legacy framework. + +#### Instantaneous Coretime + +Processing of Instantaneous Coretime happens in part on the Polkadot Relay-chain. Credit is purchased on the Coretime-chain for regular DOT tokens, and this results in a DOT-denominated Instantaneous Coretime Credit account on the Relay-chain being credited for the same amount. + +Though the Instantaneous Coretime Credit account records a balance for an account identifier (very likely controlled by a collator), it is *non-transferable* and *non-refundable*. It can only be consumed in order to purchase some Instantaneous Coretime with immediate availability. + +The Relay-chain reports this usage back to the Coretime-chain in order to allow it to reward the providers of the underlying Coretime, either the Polkadot System or owners of Bulk Coretime who contributed to the Instantaneous Coretime Pool. + +Specifically the Relay-chain is expected to be responsible for: + +- holding non-transferable, non-refundable DOT-denominated Instantaneous Coretime Credit balance information for collators. - setting and adjusting the price of Instantaneous Coretime based on usage. -- allowing collators to consume their ICM Credit at the current pricing in exchange for the ability to schedule one PoV for near-immediate usage. -- ensuring the Broker System Chain has timely accounting information on Coretime sales revenue. +- allowing collators to consume their Instantaneous Coretime Credit at the current pricing in exchange for the ability to schedule one PoV for near-immediate usage. +- ensuring the Coretime-Chain has timely accounting information on Instantaneous Coretime Sales revenue. -#### Broker System Chain +#### Coretime-chain -Also known as the *Broker-chain*, this is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with scheduling information of: +The *Coretime-chain* is a new system parachain. It has the responsibility of providing the Relay-chain via UMP with information of: -- The number of cores which should be made available during the next session. -- Which para IDs should be running on which cores and in what ratios. +- The number of cores which should be made available. +- Which tasks should be running on which cores and in what ratios. +- Accounting information for Instantaneous Coretime Credit. + +It also expects information from the Relay-chain via DMP: + +- The number of cores available to be scheduled. +- Account information on Instantaneous Coretime Sales. The specific interface is properly described in RFC-5. @@ -99,22 +117,22 @@ The specific interface is properly described in RFC-5. #### Parameters -This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, and are either *suggested* or *specified*. If *suggested*, it is non-binding and the proposal should not be judged on the value since the governance mechanism of Polkadot is expected to specify/maintain it. If *specified*, then the proposal should be judged on the merit of the value as-is. +This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, and are either *suggested* or *specified*. If *suggested*, it is non-binding and the proposal should not be judged on the value since other RFCs and/or the governance mechanism of Polkadot is expected to specify/maintain it. If *specified*, then the proposal should be judged on the merit of the value as-is. | Name | Value | | | ------------------- | ---------------------------- | ---------- | | `BULK_PERIOD` | `28 * DAYS` | specified | | `INTERLUDE_PERIOD` | `7 * DAYS` | specified | | `LEADIN_PERIOD` | `7 * DAYS` | specified | -| `STEP_PERIOD` | `8 * HOURS` | specified | | `TIMESLICE` | `8 * MINUTES` | specified | | `BULK_TARGET` | `30` | suggested | | `BULK_LIMIT` | `45` | suggested | +| `STEP_PERIOD` | `8 * HOURS` | suggested | | `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | suggested | #### Reservations -The Broker-chain includes some governance-set reservations of Coretime; these cover every System-chain as well as the pre-existing leased chains. +The Coretime-chain includes some governance-set reservations of Coretime; these cover every System-chain as well as the pre-existing leased chains. #### Regions @@ -122,37 +140,39 @@ A *Region* is an assignable period of Coretime with a known regularity. All Regions are associated with a unique *Core Index*, to identify which core the Region controls the assignment of. -All Regions are also associated with a *Core Parts*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Parts value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Part bitmap represents exactly one Core for one Relay-chain block in one Timeslice. +All Regions are also associated with a *Core Mask*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Mask value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Part bitmap represents exactly one Core for one Relay-chain block in one Timeslice. All Regions have a span. Region spans are quantized into periods of `TIMESLICE` blocks; `BULK_PERIOD` divides into `TIMESLICE` a whole number of times. -The `Timeslice` type is a `u32` which can be multiplied by `TIMESLICE` to give a `BlockNumber` value representing the same quantity in terms of (Relay-chain) blocks. +The `Timeslice` type is a `u32` which can be multiplied by `TIMESLICE` to give a `BlockNumber` value representing the same quantity in terms of Relay-chain blocks. -Regions can be tasked to a ParaId or placed in the Instantaneous Coretime Pool. If they have yet to be placed or tasked, then they are fresh. Fresh Regions have an *Owner*. +Regions can be tasked to a `TaskId` (aka `ParaId`) or pooled into the Instantaneous Coretime Pool. This process can be *Provisional* or *Final*. If done only provisionally or not at all then they are fresh and have an *Owner* which is able to manipulate them further including reassignment. Once *Final*, then all ownership information is discarded and they cannot be manipulated further. Renewal is not possible when only provisionally tasked/pooled. #### Bulk Sales -A sale of Bulk Coretime occurs on the Broker Chain every `BULK_PERIOD` blocks. +A sale of Bulk Coretime occurs on the Coretime-chain every `BULK_PERIOD` blocks. -In every sale, a `BULK_LIMIT` of individual *Regions* are offered for sale at a particular Sale Price. +In every sale, a `BULK_LIMIT` of individual *Regions* are offered for sale. Each Region offered for sale has a different Core Index, ensuring that they each represent an independently allocatable resource on the Polkadot UC. -The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin immediately following the span of the previous Sale's Regions. The Regions offered for sale also have the complete, non-interlaced, Core. +The Regions offered for sale have the same span: they last exactly `BULK_PERIOD` blocks, and begin immediately following the span of the previous Sale's Regions. The Regions offered for sale also have the complete, non-interlaced, Core Mask. -The sale period ends immediately as soon as span of the Coretime Regions that are being sold begins. At this point, the next Sale Price is set according to the previous Sale Price and the number of Regions sold compared to the desired and maximum amount of Regions to be sold. See Price Setting for additional detail. +The Sale Period ends immediately as soon as span of the Coretime Regions that are being sold begins. At this point, the next Sale Price is set according to the previous Sale Price together with the number of Regions sold compared to the desired and maximum amount of Regions to be sold. See Price Setting for additional detail on this point. -Following this, there is an *Interlude* period lasting `INTERLUDE_PERIOD` of blocks before the next Sale becomes active. After this period is elapsed, *Regular Purchasing* may begin (see next). +Following the end of the previous Sale Period, there is an *Interlude Period* lasting `INTERLUDE_PERIOD` of blocks. After this period is elapsed, regular purchasing begins with the *Purchasing Period*. This is designed to give at least two weeks worth of time for the purchased regions to be partitioned, interlaced, traded and allocated. +#### The Interlude + +The Interlude period is a period prior to Regular Purchasing where renewals are allowed to happen. This has the effect of ensuring existing long-term tasks/parachains have a chance to secure their Bulk Coretime for a well-known price prior to general sales. + #### Regular Purchasing -Any account may purchase Regions of Bulk Coretime if they have the appropriate funds in place during the *Purchasing Period*, which is from `INTERLUDE_PERIOD` blocks after the end of the previous sale until the beginning of the Region of the Bulk Coretime which is for sale as long as there are Regions of Bulk Coretime left for sale (i.e. no more than `BULK_LIMIT` have already been sold in the Bulk Coretime Sale). +Any account may purchase Regions of Bulk Coretime if they have the appropriate funds in place during the Purchasing Period, which is from `INTERLUDE_PERIOD` blocks after the end of the previous sale until the beginning of the Region of the Bulk Coretime which is for sale as long as there are Regions of Bulk Coretime left for sale (i.e. no more than `BULK_LIMIT` have already been sold in the Bulk Coretime Sale). The Purchasing Period is thus roughly `BULK_PERIOD - INTERLUDE_PERIOD` blocks in length. -The Purchasing Period is roughly `BULK_PERIOD - INTERLUDE_PERIOD` blocks in length. The price various during an initial portion of the Purchasing Period and then stays stable for the remainder. This initial portion is `LEADIN_PERIOD` blocks in length. During this period the price decreases monotonically from the Sale Price multiplied by `LEADIN_MULTIPLIER` to the basic Sale Price over a number of discrete drops, each `STEP_PERIOD` blocks in length. - -The eventual usage of the funds paid is out of scope for the present proposal. +The Sale Price varies during an initial portion of the Purchasing Period called the *Leadin Period* and then stays stable for the remainder. This initial portion is `LEADIN_PERIOD` blocks in duration. During the Leadin Period the price decreases towards the Sale Price, which it lands at by the end of the Leadin Period. The actual curve by which the price starts and descends to the Sale Price is outside the scope of this RFC, though a basic suggestion is provided in the Price Setting Notes, below. #### Renewals @@ -160,103 +180,116 @@ At any time when there are remaining Regions of Bulk Coretime to be sold, *inclu Firstly, the price paid is exactly `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous sale. -Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. +Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. As such unlike regular purchasing the Region never has an owner. -Renewal is only possible for either cores which have been assigned as a result of a previous renewal, or which begin their Bulk Coretime with a fully allocated workload which does not include allocation to the Instantaneous Coretime Pool. In the latter case, the renewed workload will be the same as this initial workload. +Renewal is only possible for either cores which have been assigned as a result of a previous renewal, which are migrating from legacy slot leases, or which fill their Bulk Coretime with an unsegmented, fully and finally assigned workload which does not include placement in the Instantaneous Coretime Pool. In the latter case, the renewed workload will be the same as this initial workload. #### Manipulation Regions may be manipulated in various ways by its owner: 1. *Transferred* in ownership. -1. *Assigned* to a single, specific ParaID task. 1. *Partitioned* into quantized, non-overlapping segments of Bulk Coretime with the same ownership. 1. *Interlaced* into multiple Regions over the same period whose eventual assignments take turns to be scheduled. +1. *Assigned* to a single, specific ParaID task. This may be either *provisional* or *final*. 1. *Pooled* into the Instantaneous Coretime Pool, in return for a pro-rata amount of the revenue from the Instantaneous Coretime Sales over its period. #### Enactment -### Specific functions of the Broker-chain +### Specific functions of the Coretime-chain -Several functions of the Broker-chain SHALL be exposed through dispatchables and/or a `nonfungible` trait implementation integrated into XCM: +Several functions of the Coretime-chain SHALL be exposed through dispatchables and/or a `nonfungible` trait implementation integrated into XCM: #### 1. `transfer` Regions may have their ownership transferred. -A `transfer(region: RegionId, new_owner: AccountId)` dispatchable SHALL have the effect of altering the current owner of the Region identified by `region` from the signed origin to `new_owner`. +A `transfer(region: RegionId, new_owner: AccountId)` dispatchable shall have the effect of altering the current owner of the Region identified by `region` from the signed origin to `new_owner`. An implementation of the `nonfungible` trait SHOULD include equivalent functionality. `RegionId` SHOULD be used for the `AssetInstance` value. -#### 2. `assign` +#### 2. `partition` -Regions may be consumed in exchange for being assigned to a core. +Regions may be split apart into two non-overlapping interior Regions of the same Core Mask which together concatenate to the original Region. -A `assign(region: RegionId, target: ParaId)` dispatchable SHALL have the effect of removing the region identified by `region` and placing an item in the workplan corresponding to the region's properties and assigned to the `target` task. - -If the region's end has already passed (taking into account any advance notice requirements) then this operation is a no-op. If the region's begining has already passed, then it is effectively altered to become the next schedulable timeslice. - -If the Region's span is the entire `BULK_PERIOD`, then the Broker-chain records in storage that the allocation happened during this period in order to facilitate the possibility for a renewal. - -Also: -- `owner` field of `region` must the equal to the Signed origin. - -#### 3. `partition` - -Regions may be split apart into two non-overlapping interior Regions of the same regularity. - -A `partition(region: RegionId, pivot: Timeslice)` dispatchable SHALL have the effect of removing the Region identified by `region` and adding two new Regions of the same owner and core-parts. One new Region will begin at the same point of the old Region but end at `pivot`, whereas the other will begin at `pivot` and end at the end point of the old Region. +A `partition(region: RegionId, pivot: Timeslice)` dispatchable SHALL have the effect of removing the Region identified by `region` and adding two new Regions of the same owner and Core Mask. One new Region will begin at the same point of the old Region but end at `pivot` timeslices into the Region, whereas the other will begin at this point and end at the end point of the original Region. Also: - `owner` field of `region` must the equal to the Signed origin. - `pivot` must equal neither the `begin` nor `end` fields of the `region`. -#### 4. `interlace` +#### 3. `interlace` -Regions may be strided into two Regions of the same span whose eventual assignments take turns on the core. +Regions may be decomposed into two Regions of the same span whose eventual assignments take turns on the core by virtue of having complementary Core Masks. -An `interlace(region: RegionId, parts: CoreParts)` dispatchable SHALL have the effect of removing the Region identified by `region` and create two new Regions. The new Regions will each have the same span and owner of the old Region, but one Region will have Core Parts equal to `parts` and the other will have Core Parts equal to the XOR of `parts` and the Core Parts of the old Region. +An `interlace(region: RegionId, mask: CoreMask)` dispatchable shall have the effect of removing the Region identified by `region` and creating two new Regions. The new Regions will each have the same span and owner of the original Region, but one Region will have a Core Mask equal to `mask` and the other will have Core Mask equal to the XOR of `mask` and the Core Mask of the original Region. + +Also: +- `owner` field of `region` must the equal to the Signed origin. +- `mask` must have some bits set AND must not equal the Core Mask of the old Region AND must only have bits set which are also set in the old Region's' Core Mask. + +#### 4. `assign` + +Regions may be assigned to a core. + +A `assign(region: RegionId, target: ParaId, finality: Finality)` dispatchable shall have the effect of placing an item in the workplan corresponding to the region's properties and assigned to the `target` task. + +If the region's end has already passed (taking into account any advance notice requirements) then this operation is a no-op. If the region's begining has already passed, then it is effectively altered to become the next schedulable timeslice. + +`finality` may have the value of either `Final` or `Provisional`. If `Final`, then the operation is free, the `region` record is removed entirely from storage and renewal may be possible: if the Region's span is the entire `BULK_PERIOD`, then the Coretime-chain records in storage that the allocation happened during this period in order to facilitate the possibility for a renewal. (Renewal only becomes possible when the full Core Mask of a core is finally assigned for the full `BULK_PERIOD`.) Also: - `owner` field of `region` must the equal to the Signed origin. -- `parts` must have some bits set AND must not equal the Core Parts of the old Region AND must only have bits set which are also set in the old Region's' Core Parts. #### 5. `pool` Regions may be consumed in exchange for a pro rata portion of the Instantaneous Coretime Sales Revenue from its period and regularity. -A `pool(region: RegionId, beneficiary: AccountId)` dispatchable SHALL have the effect of removing the region identified by `region` and placing an item in the workplan corresponding to the region's properties and assigned to the Instantaneous Coretime Pool. The details of the region will be recorded in order to allow the proper allocation of Instantaneous Coretime Sales Revenue. +A `pool(region: RegionId, beneficiary: AccountId, finality: Finality)` dispatchable shall have the effect of placing an item in the workplan corresponding to the region's properties and assigned to the Instantaneous Coretime Pool. The details of the region will be recorded in order to allow for a pro rata share of the Instantaneous Coretime Sales Revenue at the time of the Region relative to any other providers in the Pool. If the region's end has already passed (taking into account any advance notice requirements) then this operation is a no-op. If the region's begining has already passed, then it is effectively altered to become the next schedulable timeslice. +`finality` may have the value of either `Final` or `Provisional`. If `Final`, then the operation is free and the `region` record is removed entirely from storage. + Also: - `owner` field of `region` must the equal to the Signed origin. #### 6. Renewals -A dispatchable `renew(core: CoreIndex)` SHALL be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. +A dispatchable `renew(core: CoreIndex)` shall be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. This may be called during the Interlude Period as well as the regular Purchasing Period and has the same effect as `purchase` followed by `allocate`, except that: -1. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP` of itself or the regular price, whichever is lower. -1. The Region must be allocated as the given `core` was allocated at the beginning of the present Region. +1. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP`. +1. The Region is allocated exactly the given `core` is currently allocated for the present Region. -#### 7. Migrations +Renewal is only valid where a Region's span is assigned to Tasks (not placed in the Instantaneous Coretime Pool) for the entire unsplit `BULK_PERIOD` over all of the Core Mask and with Finality. There are thus three possibilities of a renewal being allowed: -It is intended that paras which hold a lease from the legacy slot auction system are able to seamlessly transition into the Agile Coretime framework. +1. Purchased unsplit Coretime with final assignment to tasks over the full Core Mask. +1. Renewed Coretime. +1. A legacy lease which is ending. -A dispatchable `migrate(core: CoreIndex)` SHALL be provided. Any account may call `migrate` to purchase Bulk Coretime at the current price for the given `core`. +#### 7. Instantaneous Coretime Credits -This MUST be called during the `LEADIN_PERIOD` prior to a Bulk sale (exactly like `purchase`) and has the same effect as `purchase` followed by `allocate` with a value of `ParaId` containing just one item equal to `target`, except that: +A dispatchable `purchase_credit(amount: Balance, beneficiary: RelayChainAccountId)` shall be provided. Any account with at least `amount` spendable funds may call this. This increases the Instantaneous Coretime Credit balance on the Relay-chain of the `beneficiary` by the given `amount`. -1. The purchase always succeeds and as such MUST be processed prior to regular `purchase` orders. -2. There MUST be an ongoing legacy parachain lease whose parachain is assigned to `core` and which is due to expire during the Coretime period being purchased. +This Credit is consumable on the Relay-chain as part of the Task scheduling system and its specifics are out of scope within this proposal. When consumed, revenue is recorded and provided to the Coretime-chain for proper distribution. The API for doing this is specified in RFC-5. -AllowedRenewals map is updated with this information ready for the following Bulk sale. +### Notes on the Instantaneous Coretime Market -### Notes on Price Setting +For an efficient market to form around the provision of Bulk-purchased Cores into the pool of cores available for Instantaneous Coretime purchase, it is crucial to ensure that price changes for the purchase of Instantaneous Coretime are reflected well in the revenues of private Coretime providers during the same period. -The specific price setting mechanism is out of scope for this proposal and should be covered in some other work. The present proposal assumes the existence of a price-setting mechanism which could take into account three parameters; two mostly fixed and one which changes each `BULK_PERIOD`. These parameters are `BULK_TARGET`, `BULK_LIMIT` and `CORES_SOLD` which is the actual number of cores sold for the present `BULK_PERIOD` and is always an unsigned integer at most `BULK_LIMIT`. +In order to ensure this, then it is crucial that Instantaneous Coretime, once purchased, cannot be held indefinitely prior to eventual use since, if this were the case, a nefarious collator could purchase Coretime when cheap and utilize it some time later when expensive and deprive private Coretime providers of their revenue. + +It must therefore be assumed that Instantaneous Coretime, once purchased, has a definite and short "shelf-life", after which it becomes unusable. This incentivizes collators to avoid purchasing Coretime unless they expect to utilize it imminently and thus helps create an efficient market-feedback mechanism whereby a higher price will actually result in material revenues for private Coretime providers who contribute to the pool of Cores available to service Instantaneous Coretime purchases. + +### Notes on Economics + +The specific pricing mechanisms are out of scope for the present proposal. Proposals on economics should be properly described and discussed in another RFC. However, for the sake of completeness, I provide some basic illustration of how price setting could potentially work. + +#### Bulk Price Progression + +The present proposal assumes the existence of a price-setting mechanism which could take into account three parameters; two mostly fixed and one which changes each `BULK_PERIOD`. These parameters are `BULK_TARGET`, `BULK_LIMIT` and `CORES_SOLD` which is the actual number of cores sold for the present `BULK_PERIOD` and is always an unsigned integer at most `BULK_LIMIT`. In general we would expect the price to increase the closer `CORES_SOLD` gets to `BULK_LIMIT` and to decrease the closer it gets to zero. If it is exactly equal to `BULK_TARGET`, then we would expect the price to remain the same. @@ -273,16 +306,46 @@ END IF This exists only as a trivial example to demonstrate a basic solution exists, and should not be intended as a concrete proposal. -### Notes on Instantaneous Core Sales +#### Intra-Leadin Price-decrease -For access to the Instantaneous Core sales we may include an `allocate_instantaneous` function. This should allocate the Coretime for usage by Polkadot to serve instantaneous requests and allow the `owner` to collect a pro-rata amount of total Instantaneous sales revenue. +During the Leadin Period of a sale, the effective price starts higher than the Sale Price and falls to end at the Sale Price at the end of the Leadin Period. The price can thus be defined as a simple factor above one on which the Sale Price is multiplied. A function which returns this factor would accept a factor between zero and one specifying the portion of the Leadin Period which has passed. -For an efficient market to form around the provision of Bulk-purchased Cores into the pool of cores available for Instantaneous Coretime purchase, it is crucial to ensure that price changes for the purchase of Instantaneous Coretime are reflected well in the revenues of private Coretime providers during the same period. +Thus we assume `SALE_PRICE`, then we can define `PRICE` as: -In order to ensure this, then it is crucial that Instantaneous Coretime, once purchased, cannot be held indefinitely prior to eventual use since, if this were the case, a nefarious collator could purchase Coretime when cheap and utilize it some time later when expensive and deprive private Coretime providers of their revenue. It SHOULD therefore be assumed that Instantaneous Coretime, once purchased, has a definite and short "shelf-life", after which it becomes unusable. This incentivizes collators to avoid purchasing Coretime unless they expect to utilize it imminently and thus helps create an efficient market-feedback mechanism whereby a higher price will actually result in material revenues for private Coretime providers who contribute to the pool of Cores available to service Instantaneous Coretime purchases. +``` +PRICE := SALE_PRICE * FACTOR((NOW - LEADIN_BEGIN) / LEADIN_PERIOD) +``` + +We can define a very simple progression where the price decreases monotonically from double the Sale Price at the beginning of the Leadin Period. + +``` +FACTOR(T) := 2 - T +``` + +#### Instantaneous Price Progression + +This proposal assumes the existence of a Relay-chain-based price-setting mechanism for the Instantaneous Coretime Market which alters from block to block, taking into account several parameters: the last price, the size of the Instantaneous Coretime Pool (in terms of cores per block) and the amount of Instantaneous Coretime waiting for processing (in terms of Core-blocks queued). + +The ideal situation is to have the size of the Instantaneous Coretime Pool be equal to some factor of the Instantaneous Coretime waiting. This allows all Instantaneous Coretime sales to be processed with some limited latency while giving limited flexibility over ordering to the Relay-chain apparatus which is needed for efficient operation. + +If we set a factor of three, and thus aim to retain a queue of Instantaneous Coretime Sales which can be processed within three Relay-chain blocks, then we would increase the price if the queue goes above three times the amount of cores available, and decrease if it goes under. + +Let us assume the values `OLD_PRICE`, `FACTOR`, `QUEUE_SIZE` and `POOL_SIZE`. A simple definition of the `NEW_PRICE` would be thus: + +``` +NEW_PRICE := IF QUEUE_SIZE < POOL_SIZE * FACTOR THEN + OLD_PRICE * 0.95 +ELSE + OLD_PRICE / 0.95 +END IF +``` + +This exists only as a trivial example to demonstrate a basic solution exists, and should not be intended as a concrete proposal. ### Notes on Types +This exists only as a short illustration of a potential technical implementation and should not be treated as anything more. + #### Regions This data schema achieves a number of goals: @@ -339,7 +402,7 @@ struct ContributionRecord { begin: Timeslice, end: Timeslice, core: CoreIndex, - parts: CoreParts, + mask: CoreMask, payee: Contributor, } type InstaPoolContribution = Map; @@ -349,18 +412,18 @@ type InstaPoolIo = Map; type PoolSize = Value; -/// Counter for the total CoreParts which could be dedicated to a pool. `u32` so we don't ever get +/// Counter for the total CoreMask which could be dedicated to a pool. `u32` so we don't ever get /// an overflow. type TotalParts = u32; struct InstaPoolHistoryRecord { total_contributions: TotalParts, maybe_payout: Option, } -/// Total InstaPool rewards for each Timeslice and the number of core parts which contributed. +/// Total InstaPool rewards for each Timeslice and the number of core Mask which contributed. type InstaPoolHistory = Map; ``` -`CoreParts` tracks what unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each compnent of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. +`CoreMask` tracks what unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each compnent of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. Regions are issued into the `Regions` map and can be transferred, partitioned and interlaced as the owner desires. Regions can only be tasked if they begin after the current scheduling deadline (if they have missed this, then the region can be auto-trimmed until it is). @@ -370,7 +433,7 @@ Each timeslice, `InstaPoolIo` is used to update the current value of `PoolSize`. When Instantaneous Coretime Market Revenues are reported for a particular timeslice from the Relay-chain, this information gets placed in the `maybe_payout` field of the relevant record of `InstaPoolHistory`. -Payments can be requested made for any records in `InstaPoolContribution` whose `begin` is the key for a value in `InstaPoolHistory` whose `maybe_payout` is `Some`. In this case, the `total_contributions` is reduced by the `ContributionRecord`'s `parts` and a pro rata amount paid. The `ContributionRecord` is mutated by incrementing `begin`, or removed if `begin` becomes equal to `end`. +Payments can be requested made for any records in `InstaPoolContribution` whose `begin` is the key for a value in `InstaPoolHistory` whose `maybe_payout` is `Some`. In this case, the `total_contributions` is reduced by the `ContributionRecord`'s `mask` and a pro rata amount paid. The `ContributionRecord` is mutated by incrementing `begin`, or removed if `begin` becomes equal to `end`. Example: @@ -429,7 +492,7 @@ Workplan: (110, 0) => vec![{ part: 0b0000_0000_1111_1111u16, task: Assigned(B) }] (150, 0) => vec![{ part: 0b1111_1111_1111_1111u16, task: InstaPool }] InstaPoolContribution: -{ begin: 150, end: 200, core: 0, parts: 0b1111_1111_1111_1111u16, payee: Alice } +{ begin: 150, end: 200, core: 0, mask: 0b1111_1111_1111_1111u16, payee: Alice } InstaPoolIo: 150 => 16 200 => -16 @@ -508,7 +571,7 @@ Rollout of this proposal comes in several phases: No specific considerations. -Parachains already deployed into the Polkadot UC MUST have a clear plan of action to migrate to an agile Coretime market. +Parachains already deployed into the Polkadot UC must have a clear plan of action to migrate to an agile Coretime market. While this proposal does not introduce documentable features per se, adequate documentation must be provided to potential purchasers of Polkadot Coretime. This SHOULD include any alterations to the Polkadot-SDK software collection, most likely Cumulus. @@ -528,11 +591,20 @@ RFC-3 proposes a means of implementing the high-level allocations within the Rel RFC-5 proposes the API for interacting with Relay-chain. -Additional work should specify the interface for the instantaneous market revenue so that the Broker chain can ensure Bulk Coretime placed in the instantaneous market is properly compensated. +Additional work should specify the interface for the instantaneous market revenue so that the Coretime-chain can ensure Bulk Coretime placed in the instantaneous market is properly compensated. ## Drawbacks, Alternatives and Unknowns -None at present. +Unknowns include the economic and resource parameterisations: + +- The initial price of Bulk Coretime. +- The price-change algorithm between Bulk Coretime sales. +- The price increase per Bulk Coretime period for renewals. +- The price decrease graph in the Leadin period for Bulk Coretime sales. +- The initial price of Instantaneous Coretime. +- The price-change algorithm for Instantaneous Coretime sales. +- The percentage of cores to be sold as Bulk Coretime. +- The fate of revenue collected. ## Prior Art and References From 56c2bf4cb1d0ae0edf7c98e2cf462c24b730686c Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 18 Jul 2023 12:12:06 +0200 Subject: [PATCH 28/44] Update example pricing with sellout price and edge cases --- text/0001-agile-coretime.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index b2350a4..ca0559f 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -289,17 +289,33 @@ The specific pricing mechanisms are out of scope for the present proposal. Propo #### Bulk Price Progression -The present proposal assumes the existence of a price-setting mechanism which could take into account three parameters; two mostly fixed and one which changes each `BULK_PERIOD`. These parameters are `BULK_TARGET`, `BULK_LIMIT` and `CORES_SOLD` which is the actual number of cores sold for the present `BULK_PERIOD` and is always an unsigned integer at most `BULK_LIMIT`. +The present proposal assumes the existence of a price-setting mechanism which takes into account several parameters: + +- `OLD_PRICE`: The price of the previous sale. +- `BULK_TARGET`: the target number of cores to be purchased as Bulk Coretime Regions or renewed during the previous sale. +- `BULK_LIMIT`: the maximum number of cores which could have been purchased/renewed during the previous sale. +- `CORES_SOLD`: the actual number of cores purchased/renewed in the previous sale. +- `SELLOUT_PRICE`: the price at which the most recent Bulk Coretime was purchased (*not* renewed) prior to selling more cores than `BULK_TARGET` (or immediately after, if none were purchased before). This may not have a value if no Bulk Coretime was purchased. In general we would expect the price to increase the closer `CORES_SOLD` gets to `BULK_LIMIT` and to decrease the closer it gets to zero. If it is exactly equal to `BULK_TARGET`, then we would expect the price to remain the same. +In the edge case that no cores were purchased yet more cores were sold (through renewals) than the target, then we would also avoid altering the price. + A simple example of this would be the formula: ``` -NEW_PRICE := IF CORES_SOLD < BULK_TARGET THEN - OLD_PRICE * MAX(CORES_SOLD, 1) / BULK_TARGET +IF SELLOUT_PRICE == NULL AND CORES_SOLD > BULK_TARGET THEN + RETURN OLD_PRICE +END IF +EFFECTIVE_PRICE := IF CORES_SOLD > BULK_TARGET THEN + SELLOUT_PRICE ELSE - OLD_PRICE + OLD_PRICE * + OLD_PRICE +END IF +NEW_PRICE := IF CORES_SOLD < BULK_TARGET THEN + EFFECTIVE_PRICE * MAX(CORES_SOLD, 1) / BULK_TARGET +ELSE + EFFECTIVE_PRICE + EFFECTIVE_PRICE * (CORES_SOLD - BULK_TARGET) / (BULK_LIMIT - BULK_TARGET) END IF ``` From 34a958151520fceea4f3a1d0316740cc07539e33 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 18 Jul 2023 12:22:13 +0200 Subject: [PATCH 29/44] Use Mask terminology --- text/0001-agile-coretime.md | 98 ++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index ca0559f..6f9e629 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -140,7 +140,7 @@ A *Region* is an assignable period of Coretime with a known regularity. All Regions are associated with a unique *Core Index*, to identify which core the Region controls the assignment of. -All Regions are also associated with a *Core Mask*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Mask value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Part bitmap represents exactly one Core for one Relay-chain block in one Timeslice. +All Regions are also associated with a *Core Mask*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Mask value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Mask bitmap represents exactly one Core for one Relay-chain block in one Timeslice. All Regions have a span. Region spans are quantized into periods of `TIMESLICE` blocks; `BULK_PERIOD` divides into `TIMESLICE` a whole number of times. @@ -376,13 +376,13 @@ This data schema achieves a number of goals: ```rust type Timeslice = u32; // 80 block amounts. type CoreIndex = u16; -type CorePart = [u8; 10]; // 80-bit bitmap. +type CoreMask = [u8; 10]; // 80-bit bitmap. // 128-bit (16 bytes) struct RegionId { begin: Timeslice, core: CoreIndex, - part: CorePart, + mask: CoreMask, } // 296-bit (37 bytes) struct RegionRecord { @@ -400,7 +400,7 @@ enum CoreTask { } // 113-bit (14 bytes). Could be 14 bytes with a specialised 32-bit `CoreTask`. struct ScheduleItem { - part: CorePart, // 80 bit + mask: CoreMask, // 80 bit task: CoreTask, // 33 bit } @@ -423,23 +423,23 @@ struct ContributionRecord { } type InstaPoolContribution = Map; -type SignedTotalParts = u32; -type InstaPoolIo = Map; +type SignedTotalMaskBits = u32; +type InstaPoolIo = Map; -type PoolSize = Value; +type PoolSize = Value; /// Counter for the total CoreMask which could be dedicated to a pool. `u32` so we don't ever get /// an overflow. -type TotalParts = u32; +type TotalMaskBits = u32; struct InstaPoolHistoryRecord { - total_contributions: TotalParts, + total_contributions: TotalMaskBits, maybe_payout: Option, } /// Total InstaPool rewards for each Timeslice and the number of core Mask which contributed. type InstaPoolHistory = Map; ``` -`CoreMask` tracks what unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each compnent of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. +`CoreMask` tracks unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each compnent of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. Regions are issued into the `Regions` map and can be transferred, partitioned and interlaced as the owner desires. Regions can only be tasked if they begin after the current scheduling deadline (if they have missed this, then the region can be auto-trimmed until it is). @@ -454,59 +454,59 @@ Payments can be requested made for any records in `InstaPoolContribution` whose Example: ```rust -// Simple example with a `u16` `CorePart` and bulk sold in 100 timeslices. +// Simple example with a `u16` `CoreMask` and bulk sold in 100 timeslices. Regions: -{ core: 0u16, begin: 100, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // First split @ 50 Regions: -{ core: 0u16, begin: 100, part: 0b1111_1111_1111_1111u16 } => { end: 150u32, owner: Alice }; -{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b1111_1111_1111_1111u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 150, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Share half of first 50 blocks Regions: -{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; -{ core: 0u16, begin: 100, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Alice }; -{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 150, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Sell half of them to Bob Regions: -{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; -{ core: 0u16, begin: 100, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; -{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; +{ core: 0u16, begin: 150, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Bob splits first 10 and assigns them to himself. Regions: -{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; -{ core: 0u16, begin: 100, part: 0b0000_0000_1111_1111u16 } => { end: 110u32, owner: Bob }; -{ core: 0u16, begin: 110, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; -{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b0000_0000_1111_1111u16 } => { end: 110u32, owner: Bob }; +{ core: 0u16, begin: 110, mask: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; +{ core: 0u16, begin: 150, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Bob shares first 10 3 ways and sells smaller shares to Charlie and Dave Regions: -{ core: 0u16, begin: 100, part: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; -{ core: 0u16, begin: 100, part: 0b0000_0000_1100_0000u16 } => { end: 110u32, owner: Charlie }; -{ core: 0u16, begin: 100, part: 0b0000_0000_0011_0000u16 } => { end: 110u32, owner: Dave }; -{ core: 0u16, begin: 100, part: 0b0000_0000_0000_1111u16 } => { end: 110u32, owner: Bob }; -{ core: 0u16, begin: 110, part: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; -{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b1111_1111_0000_0000u16 } => { end: 150u32, owner: Alice }; +{ core: 0u16, begin: 100, mask: 0b0000_0000_1100_0000u16 } => { end: 110u32, owner: Charlie }; +{ core: 0u16, begin: 100, mask: 0b0000_0000_0011_0000u16 } => { end: 110u32, owner: Dave }; +{ core: 0u16, begin: 100, mask: 0b0000_0000_0000_1111u16 } => { end: 110u32, owner: Bob }; +{ core: 0u16, begin: 110, mask: 0b0000_0000_1111_1111u16 } => { end: 150u32, owner: Bob }; +{ core: 0u16, begin: 150, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; // Bob assigns to his para B, Charlie and Dave assign to their paras C and D; Alice assigns first 50 to A Regions: -{ core: 0u16, begin: 150, part: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; +{ core: 0u16, begin: 150, mask: 0b1111_1111_1111_1111u16 } => { end: 200u32, owner: Alice }; Workplan: (100, 0) => vec![ - { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, - { part: 0b0000_0000_1100_0000u16, task: Assigned(C) }, - { part: 0b0000_0000_0011_0000u16, task: Assigned(D) }, - { part: 0b0000_0000_0000_1111u16, task: Assigned(B) }, + { mask: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { mask: 0b0000_0000_1100_0000u16, task: Assigned(C) }, + { mask: 0b0000_0000_0011_0000u16, task: Assigned(D) }, + { mask: 0b0000_0000_0000_1111u16, task: Assigned(B) }, ] -(110, 0) => vec![{ part: 0b0000_0000_1111_1111u16, task: Assigned(B) }] +(110, 0) => vec![{ mask: 0b0000_0000_1111_1111u16, task: Assigned(B) }] // Alice assigns her remaining 50 timeslices to the InstaPool paying herself: Regions: (empty) Workplan: (100, 0) => vec![ - { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, - { part: 0b0000_0000_1100_0000u16, task: Assigned(C) }, - { part: 0b0000_0000_0011_0000u16, task: Assigned(D) }, - { part: 0b0000_0000_0000_1111u16, task: Assigned(B) }, + { mask: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { mask: 0b0000_0000_1100_0000u16, task: Assigned(C) }, + { mask: 0b0000_0000_0011_0000u16, task: Assigned(D) }, + { mask: 0b0000_0000_0000_1111u16, task: Assigned(B) }, ] -(110, 0) => vec![{ part: 0b0000_0000_1111_1111u16, task: Assigned(B) }] -(150, 0) => vec![{ part: 0b1111_1111_1111_1111u16, task: InstaPool }] +(110, 0) => vec![{ mask: 0b0000_0000_1111_1111u16, task: Assigned(B) }] +(150, 0) => vec![{ mask: 0b1111_1111_1111_1111u16, task: InstaPool }] InstaPoolContribution: { begin: 150, end: 200, core: 0, mask: 0b1111_1111_1111_1111u16, payee: Alice } InstaPoolIo: @@ -524,25 +524,25 @@ PoolSize: 0 // Block 990: Relay <= assign_core(core: 0u16, begin: 1000, assignment: vec![(A, 8), (C, 2), (D, 2), (B, 4)]) Workload: 0 => vec![ - { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, - { part: 0b0000_0000_1100_0000u16, task: Assigned(C) }, - { part: 0b0000_0000_0011_0000u16, task: Assigned(D) }, - { part: 0b0000_0000_0000_1111u16, task: Assigned(B) }, + { mask: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { mask: 0b0000_0000_1100_0000u16, task: Assigned(C) }, + { mask: 0b0000_0000_0011_0000u16, task: Assigned(D) }, + { mask: 0b0000_0000_0000_1111u16, task: Assigned(B) }, ] PoolSize: 0 // Block 1090: Relay <= assign_core(core: 0u16, begin: 1100, assignment: vec![(A, 8), (B, 8)]) Workload: 0 => vec![ - { part: 0b1111_1111_0000_0000u16, task: Assigned(A) }, - { part: 0b0000_0000_1111_1111u16, task: Assigned(B) }, + { mask: 0b1111_1111_0000_0000u16, task: Assigned(A) }, + { mask: 0b0000_0000_1111_1111u16, task: Assigned(B) }, ] PoolSize: 0 // Block 1490: Relay <= assign_core(core: 0u16, begin: 1500, assignment: vec![(Pool, 16)]) Workload: 0 => vec![ - { part: 0b1111_1111_1111_1111u16, task: InstaPool }, + { mask: 0b1111_1111_1111_1111u16, task: InstaPool }, ] PoolSize: 16 InstaPoolIo: From 36ee156136484b9ad2730259ca2983b4869846cb Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 25 Jul 2023 11:40:56 +0100 Subject: [PATCH 30/44] Update text/0001-agile-coretime.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 6f9e629..222af41 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -589,7 +589,7 @@ No specific considerations. Parachains already deployed into the Polkadot UC must have a clear plan of action to migrate to an agile Coretime market. -While this proposal does not introduce documentable features per se, adequate documentation must be provided to potential purchasers of Polkadot Coretime. This SHOULD include any alterations to the Polkadot-SDK software collection, most likely Cumulus. +While this proposal does not introduce documentable features per se, adequate documentation must be provided to potential purchasers of Polkadot Coretime. This SHOULD include any alterations to the Polkadot-SDK software collection. ## Testing, Security and Privacy From 0c3121f6c286d2f793d3579c10d61f38f6ff0d6b Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 25 Jul 2023 11:41:39 +0100 Subject: [PATCH 31/44] Update text/0001-agile-coretime.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0001-agile-coretime.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 222af41..81e7332 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -398,10 +398,10 @@ enum CoreTask { Assigned { target: ParaId }, InstaPool, } -// 113-bit (14 bytes). Could be 14 bytes with a specialised 32-bit `CoreTask`. +// 120-bit (15 bytes). Could be 14 bytes with a specialised 32-bit `CoreTask`. struct ScheduleItem { mask: CoreMask, // 80 bit - task: CoreTask, // 33 bit + task: CoreTask, // 40 bit } /// The work we plan on having each core do at a particular time in the future. From f939d757a8732be4726b9f1bd517d9f2fb9f7cc9 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 25 Jul 2023 11:49:18 +0100 Subject: [PATCH 32/44] Update text/0001-agile-coretime.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 81e7332..a99a521 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -340,7 +340,7 @@ FACTOR(T) := 2 - T #### Instantaneous Price Progression -This proposal assumes the existence of a Relay-chain-based price-setting mechanism for the Instantaneous Coretime Market which alters from block to block, taking into account several parameters: the last price, the size of the Instantaneous Coretime Pool (in terms of cores per block) and the amount of Instantaneous Coretime waiting for processing (in terms of Core-blocks queued). +This proposal assumes the existence of a Relay-chain-based price-setting mechanism for the Instantaneous Coretime Market which alters from block to block, taking into account several parameters: the last price, the size of the Instantaneous Coretime Pool (in terms of cores per Relay-chain block) and the amount of Instantaneous Coretime waiting for processing (in terms of Core-blocks queued). The ideal situation is to have the size of the Instantaneous Coretime Pool be equal to some factor of the Instantaneous Coretime waiting. This allows all Instantaneous Coretime sales to be processed with some limited latency while giving limited flexibility over ordering to the Relay-chain apparatus which is needed for efficient operation. From 056cc23357641b90a8b54a465f7129c34d9f2ee2 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 25 Jul 2023 12:13:18 +0100 Subject: [PATCH 33/44] Update text/0001-agile-coretime.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index a99a521..b5ad69a 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -93,7 +93,7 @@ The Relay-chain reports this usage back to the Coretime-chain in order to allow Specifically the Relay-chain is expected to be responsible for: -- holding non-transferable, non-refundable DOT-denominated Instantaneous Coretime Credit balance information for collators. +- holding non-transferable, non-refundable DOT-denominated Instantaneous Coretime Credit balance information. - setting and adjusting the price of Instantaneous Coretime based on usage. - allowing collators to consume their Instantaneous Coretime Credit at the current pricing in exchange for the ability to schedule one PoV for near-immediate usage. - ensuring the Coretime-Chain has timely accounting information on Instantaneous Coretime Sales revenue. From fae6b26276ced7f62131b2927eb60427890106c4 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 25 Jul 2023 12:15:04 +0100 Subject: [PATCH 34/44] Update text/0001-agile-coretime.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index b5ad69a..aec6244 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -182,7 +182,7 @@ Firstly, the price paid is exactly `RENEWAL_PRICE_CAP` more than what the purcha Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. As such unlike regular purchasing the Region never has an owner. -Renewal is only possible for either cores which have been assigned as a result of a previous renewal, which are migrating from legacy slot leases, or which fill their Bulk Coretime with an unsegmented, fully and finally assigned workload which does not include placement in the Instantaneous Coretime Pool. In the latter case, the renewed workload will be the same as this initial workload. +Renewal is only possible for either cores which have been assigned as a result of a previous renewal, which are migrating from legacy slot leases, or which fill their Bulk Coretime with an unsegmented, fully and finally assigned workload which does not include placement in the Instantaneous Coretime Pool. The renewed workload will be the same as this initial workload. #### Manipulation From 532ceadf0819ea235508be6fb6c8e01e340b6eed Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 25 Jul 2023 12:24:44 +0100 Subject: [PATCH 35/44] Update text/0001-agile-coretime.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index aec6244..b750fc4 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -258,7 +258,7 @@ Also: A dispatchable `renew(core: CoreIndex)` shall be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. -This may be called during the Interlude Period as well as the regular Purchasing Period and has the same effect as `purchase` followed by `allocate`, except that: +This may be called during the Interlude Period as well as the regular Purchasing Period and has the same effect as `purchase` followed by `assign`, except that: 1. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP`. 1. The Region is allocated exactly the given `core` is currently allocated for the present Region. From 5fd1c379059a37477ab991ef240aa9fa7d88103a Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 25 Jul 2023 13:17:23 +0100 Subject: [PATCH 36/44] Address grumbles. --- text/0001-agile-coretime.md | 65 +++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index b750fc4..8640d8a 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -117,22 +117,11 @@ The specific interface is properly described in RFC-5. #### Parameters -This proposal includes a number of parameters which need not necessarily be fixed. They are stated here along with a value, and are either *suggested* or *specified*. If *suggested*, it is non-binding and the proposal should not be judged on the value since other RFCs and/or the governance mechanism of Polkadot is expected to specify/maintain it. If *specified*, then the proposal should be judged on the merit of the value as-is. +This proposal includes a number of parameters which need not necessarily be fixed. Their usage is explained below, but their values are suggested or specified in the later section *Parameter Values*. -| Name | Value | | -| ------------------- | ---------------------------- | ---------- | -| `BULK_PERIOD` | `28 * DAYS` | specified | -| `INTERLUDE_PERIOD` | `7 * DAYS` | specified | -| `LEADIN_PERIOD` | `7 * DAYS` | specified | -| `TIMESLICE` | `8 * MINUTES` | specified | -| `BULK_TARGET` | `30` | suggested | -| `BULK_LIMIT` | `45` | suggested | -| `STEP_PERIOD` | `8 * HOURS` | suggested | -| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | suggested | +#### Reservations and Leases -#### Reservations - -The Coretime-chain includes some governance-set reservations of Coretime; these cover every System-chain as well as the pre-existing leased chains. +The Coretime-chain includes some governance-set reservations of Coretime; these cover every System-chain. Additionally, governance is expected to initialize details of the pre-existing leased chains. #### Regions @@ -191,7 +180,7 @@ Regions may be manipulated in various ways by its owner: 1. *Transferred* in ownership. 1. *Partitioned* into quantized, non-overlapping segments of Bulk Coretime with the same ownership. 1. *Interlaced* into multiple Regions over the same period whose eventual assignments take turns to be scheduled. -1. *Assigned* to a single, specific ParaID task. This may be either *provisional* or *final*. +1. *Assigned* to a single, specific task (identified by `TaskId` aka `ParaId`). This may be either *provisional* or *final*. 1. *Pooled* into the Instantaneous Coretime Pool, in return for a pro-rata amount of the revenue from the Instantaneous Coretime Sales over its period. #### Enactment @@ -232,7 +221,7 @@ Also: Regions may be assigned to a core. -A `assign(region: RegionId, target: ParaId, finality: Finality)` dispatchable shall have the effect of placing an item in the workplan corresponding to the region's properties and assigned to the `target` task. +A `assign(region: RegionId, target: TaskId, finality: Finality)` dispatchable shall have the effect of placing an item in the workplan corresponding to the region's properties and assigned to the `target` task. If the region's end has already passed (taking into account any advance notice requirements) then this operation is a no-op. If the region's begining has already passed, then it is effectively altered to become the next schedulable timeslice. @@ -254,13 +243,26 @@ If the region's end has already passed (taking into account any advance notice r Also: - `owner` field of `region` must the equal to the Signed origin. -#### 6. Renewals +#### 6. Purchases + +A dispatchable `purchase(price_limit: Balance)` shall be provided. Any account may call `purchase` to purchase Bulk Coretime at the maximum price of `price_limit`. + +This may be called successfully only: + +1. during the regular Purchasing Period; +2. when the caller is a Signed origin and their account balance is reducible by the current sale price; +3. when the current sale price is no greater than `price_limit`; and +4. when the number of cores already sold is less than `BULK_LIMIT`. + +If successful, the caller's account balance is reduced by the current sale price and a new Region item for the following Bulk Coretime span is issued with the owner equal to the caller's account. + +#### 7. Renewals A dispatchable `renew(core: CoreIndex)` shall be provided. Any account may call `renew` to purchase Bulk Coretime and renew an active allocation for the given `core`. This may be called during the Interlude Period as well as the regular Purchasing Period and has the same effect as `purchase` followed by `assign`, except that: -1. The price of the purchase is the previous `price` incremented by `RENEWAL_PRICE_CAP`. +1. The price of the sale is the Renewal Price (see next). 1. The Region is allocated exactly the given `core` is currently allocated for the present Region. Renewal is only valid where a Region's span is assigned to Tasks (not placed in the Instantaneous Coretime Pool) for the entire unsplit `BULK_PERIOD` over all of the Core Mask and with Finality. There are thus three possibilities of a renewal being allowed: @@ -269,7 +271,15 @@ Renewal is only valid where a Region's span is assigned to Tasks (not placed in 1. Renewed Coretime. 1. A legacy lease which is ending. -#### 7. Instantaneous Coretime Credits +**Renewal Price** + +The Renewal Price is: + +- If the workload being renewed came to be through the *Purchase and Assignment* of Bulk Coretime, then the price paid during the Purchase operation. +- If the workload being renewed was previously renewed, then the price paid during this previous Renewal operation plus `RENEWAL_PRICE_CAP`. +- If the workload being renewed is a migation from a legacy slot auction lease, then the nominal price for a Regular Purchase (outside of the Lead-in Period) of the Sale during which the legacy lease expired. + +#### 8. Instantaneous Coretime Credits A dispatchable `purchase_credit(amount: Balance, beneficiary: RelayChainAccountId)` shall be provided. Any account with at least `amount` spendable funds may call this. This increases the Instantaneous Coretime Credit balance on the Relay-chain of the `beneficiary` by the given `amount`. @@ -338,6 +348,21 @@ We can define a very simple progression where the price decreases monotonically FACTOR(T) := 2 - T ``` +#### Parameter Values + +Parameters are either *suggested* or *specified*. If *suggested*, it is non-binding and the proposal should not be judged on the value since other RFCs and/or the governance mechanism of Polkadot is expected to specify/maintain it. If *specified*, then the proposal should be judged on the merit of the value as-is. + +| Name | Value | | +| ------------------- | ---------------------------- | ---------- | +| `BULK_PERIOD` | `28 * DAYS` | specified | +| `INTERLUDE_PERIOD` | `7 * DAYS` | specified | +| `LEADIN_PERIOD` | `7 * DAYS` | specified | +| `TIMESLICE` | `8 * MINUTES` | specified | +| `BULK_TARGET` | `30` | suggested | +| `BULK_LIMIT` | `45` | suggested | +| `RENEWAL_PRICE_CAP` | `Perbill::from_percent(2)` | suggested | + + #### Instantaneous Price Progression This proposal assumes the existence of a Relay-chain-based price-setting mechanism for the Instantaneous Coretime Market which alters from block to block, taking into account several parameters: the last price, the size of the Instantaneous Coretime Pool (in terms of cores per Relay-chain block) and the amount of Instantaneous Coretime waiting for processing (in terms of Core-blocks queued). @@ -395,7 +420,7 @@ map Regions = Map; // 40-bit (5 bytes). Could be 32-bit with a more specialised type. enum CoreTask { Off, - Assigned { target: ParaId }, + Assigned { target: TaskId }, InstaPool, } // 120-bit (15 bytes). Could be 14 bytes with a specialised 32-bit `CoreTask`. From 13166eaa824d817ad7faa12871a3600373885e0c Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Mon, 31 Jul 2023 16:30:51 +0100 Subject: [PATCH 37/44] Update text/0001-agile-coretime.md --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 8640d8a..dd1a6d2 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -464,7 +464,7 @@ struct InstaPoolHistoryRecord { type InstaPoolHistory = Map; ``` -`CoreMask` tracks unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each compnent of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. +`CoreMask` tracks unique "parts" of a single core. It is used with interlacing in order to give a unique identifier to each component of any possible interlacing configuration of a core, allowing for simple self-describing keys for all core ownership and allocation information. It also allows for each core's workload to be tracked and updated progressively, keeping ongoing compute costs well-bounded and low. Regions are issued into the `Regions` map and can be transferred, partitioned and interlaced as the owner desires. Regions can only be tasked if they begin after the current scheduling deadline (if they have missed this, then the region can be auto-trimmed until it is). From e50e717a0d85323adf60747a9f975e4936ed3f54 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 31 Jul 2023 16:45:30 +0100 Subject: [PATCH 38/44] Renewal price is only a cap --- text/0001-agile-coretime.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 8640d8a..5024e6e 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -167,7 +167,7 @@ The Sale Price varies during an initial portion of the Purchasing Period called At any time when there are remaining Regions of Bulk Coretime to be sold, *including during the Interlude Period*, then certain Bulk Coretime assignmnents may be *Renewed*. This is similar to a purchase in that funds must be paid and it consumes one of the Regions of Bulk Coretime which would otherwise be placed for purchase. However there are two key differences. -Firstly, the price paid is exactly `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous sale. +Firstly, the price paid is the minimum of `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous sale and the current (or initial, if you to begin) regular purchase price. Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. As such unlike regular purchasing the Region never has an owner. @@ -273,11 +273,11 @@ Renewal is only valid where a Region's span is assigned to Tasks (not placed in **Renewal Price** -The Renewal Price is: +The Renewal Price is the minimum of the current regular purchase price (or the initial purchase price if in the Interlude Period) and: -- If the workload being renewed came to be through the *Purchase and Assignment* of Bulk Coretime, then the price paid during the Purchase operation. +- If the workload being renewed came to be through the *Purchase and Assignment* of Bulk Coretime, then the price paid during that Purchase operation. - If the workload being renewed was previously renewed, then the price paid during this previous Renewal operation plus `RENEWAL_PRICE_CAP`. -- If the workload being renewed is a migation from a legacy slot auction lease, then the nominal price for a Regular Purchase (outside of the Lead-in Period) of the Sale during which the legacy lease expired. +- If the workload being renewed is a migation from a legacy slot auction lease, then the nominal price for a Regular Purchase (outside of the Lead-in Period) of the Sale during which the legacy lease expires. #### 8. Instantaneous Coretime Credits From b39e7f8e9112d097a6332218aca5cfc550ae6667 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 31 Jul 2023 16:50:53 +0100 Subject: [PATCH 39/44] Better wording --- text/0001-agile-coretime.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 118fd11..3cf8a59 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -167,7 +167,7 @@ The Sale Price varies during an initial portion of the Purchasing Period called At any time when there are remaining Regions of Bulk Coretime to be sold, *including during the Interlude Period*, then certain Bulk Coretime assignmnents may be *Renewed*. This is similar to a purchase in that funds must be paid and it consumes one of the Regions of Bulk Coretime which would otherwise be placed for purchase. However there are two key differences. -Firstly, the price paid is the minimum of `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous sale and the current (or initial, if you to begin) regular purchase price. +Firstly, the price paid is the minimum of `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous renewal and the current (or initial, if you to begin) regular Sale Price. Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. As such unlike regular purchasing the Region never has an owner. @@ -273,7 +273,7 @@ Renewal is only valid where a Region's span is assigned to Tasks (not placed in **Renewal Price** -The Renewal Price is the minimum of the current regular purchase price (or the initial purchase price if in the Interlude Period) and: +The Renewal Price is the minimum of the current regular Sale Price (or the initial Sale Price if in the Interlude Period) and: - If the workload being renewed came to be through the *Purchase and Assignment* of Bulk Coretime, then the price paid during that Purchase operation. - If the workload being renewed was previously renewed, then the price paid during this previous Renewal operation plus `RENEWAL_PRICE_CAP`. From c9ae9290e718e5bf20c6e8adb959fe9f71ae9d7d Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 31 Jul 2023 16:56:23 +0100 Subject: [PATCH 40/44] Better wording --- text/0001-agile-coretime.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 3cf8a59..78a9c88 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -127,9 +127,9 @@ The Coretime-chain includes some governance-set reservations of Coretime; these A *Region* is an assignable period of Coretime with a known regularity. -All Regions are associated with a unique *Core Index*, to identify which core the Region controls the assignment of. +All Regions are associated with a unique *Core Index*, to identify which core the assignment of which ownership of the Region controls. -All Regions are also associated with a *Core Mask*, an 80-bit bitmap, to denote the regularity on which it may be scheduled on the core. If all bits are set in the Core Mask value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Mask bitmap represents exactly one Core for one Relay-chain block in one Timeslice. +All Regions are also associated with a *Core Mask*, an 80-bit bitmap, to denote the regularity at which it may be scheduled on the core. If all bits are set in the Core Mask value, it is said to be *Complete*. 80 is selected since this results in the size of the datatype used to identify any Region of Polkadot Coretime to be a very convenient 128-bit. Additionally, if `TIMESLICE` (the number of Relay-chain blocks in a Timeslice) is 80, then a single bit in the Core Mask bitmap represents exactly one Core for one Relay-chain block in one Timeslice. All Regions have a span. Region spans are quantized into periods of `TIMESLICE` blocks; `BULK_PERIOD` divides into `TIMESLICE` a whole number of times. From 28aed6947bfbf88df363d67fdeb82ae1b550c58f Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 31 Jul 2023 17:01:41 +0100 Subject: [PATCH 41/44] Typo --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 78a9c88..99622a6 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -167,7 +167,7 @@ The Sale Price varies during an initial portion of the Purchasing Period called At any time when there are remaining Regions of Bulk Coretime to be sold, *including during the Interlude Period*, then certain Bulk Coretime assignmnents may be *Renewed*. This is similar to a purchase in that funds must be paid and it consumes one of the Regions of Bulk Coretime which would otherwise be placed for purchase. However there are two key differences. -Firstly, the price paid is the minimum of `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous renewal and the current (or initial, if you to begin) regular Sale Price. +Firstly, the price paid is the minimum of `RENEWAL_PRICE_CAP` more than what the purchase/renewal price was in the previous renewal and the current (or initial, if yet to begin) regular Sale Price. Secondly, the purchased Region comes preassigned with exactly the same workload as before. It cannot be traded, repartitioned, interlaced or exchanged. As such unlike regular purchasing the Region never has an owner. From b756283d409a5d5a6974923d1bf0633ed8356742 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 31 Jul 2023 17:03:26 +0100 Subject: [PATCH 42/44] Typo --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 99622a6..40b3b40 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -283,7 +283,7 @@ The Renewal Price is the minimum of the current regular Sale Price (or the initi A dispatchable `purchase_credit(amount: Balance, beneficiary: RelayChainAccountId)` shall be provided. Any account with at least `amount` spendable funds may call this. This increases the Instantaneous Coretime Credit balance on the Relay-chain of the `beneficiary` by the given `amount`. -This Credit is consumable on the Relay-chain as part of the Task scheduling system and its specifics are out of scope within this proposal. When consumed, revenue is recorded and provided to the Coretime-chain for proper distribution. The API for doing this is specified in RFC-5. +This Credit is consumable on the Relay-chain as part of the Task scheduling system and its specifics are out of the scope of this proposal. When consumed, revenue is recorded and provided to the Coretime-chain for proper distribution. The API for doing this is specified in RFC-5. ### Notes on the Instantaneous Coretime Market From 095884e206b1a0bb2e4fd5f431aac5684b4d47b4 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Aug 2023 00:43:40 +0100 Subject: [PATCH 43/44] Add reference --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 40b3b40..1d97b32 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -649,4 +649,4 @@ Unknowns include the economic and resource parameterisations: ## Prior Art and References -None. +Robert Habermeier initially wrote on the subject of reframing Polkadot as a blockspace-centric service during 2022 in the article [Polkadot Blockspace over Blockchains](https://www.rob.tech/polkadot-blockspace-over-blockchains/). This article contained the idea of introducing the possibility of transferal of "claims on execution cores" and as such "create a secondary market for Polkadot blockspace". \ No newline at end of file From 6f29561a4747bbfd95307ce75cd949dfff359e39 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Aug 2023 00:45:39 +0100 Subject: [PATCH 44/44] Rephrase according to comment --- text/0001-agile-coretime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0001-agile-coretime.md b/text/0001-agile-coretime.md index 1d97b32..b3c30df 100644 --- a/text/0001-agile-coretime.md +++ b/text/0001-agile-coretime.md @@ -649,4 +649,4 @@ Unknowns include the economic and resource parameterisations: ## Prior Art and References -Robert Habermeier initially wrote on the subject of reframing Polkadot as a blockspace-centric service during 2022 in the article [Polkadot Blockspace over Blockchains](https://www.rob.tech/polkadot-blockspace-over-blockchains/). This article contained the idea of introducing the possibility of transferal of "claims on execution cores" and as such "create a secondary market for Polkadot blockspace". \ No newline at end of file +Robert Habermeier initially wrote on the subject of Polkadot blockspace-centric in the article [Polkadot Blockspace over Blockchains](https://www.rob.tech/polkadot-blockspace-over-blockchains/). While not going into details, the article served as an early reframing piece for moving beyond one-slot-per-chain models and building out secondary market infrastructure for resource allocation. \ No newline at end of file