Agile Coretime Pricing Explained
Coretime is specified in RFC-1, but only briefly explains an example pricing model and specifically states that the described model should only illustrate that a solution exists, but should not be treated as a concrete proposal.
This blog posts explains a concrete proposal we want to launch on Kusama and if all goes well also on Polkadot.
So let's start with requirements.
Requirements on the model
A very basic requirement of any auction is to be able to find the right price. For coretime we go with a variant of the Dutch auction for its simplicity and its ability to handle selling of multiple items of the same good at once. Given enough time and fine enough price resolution front-running should also be very limited.
Another requirement we have is that we are selling core time and a major consumer of blocktime are heavy duty blockchains for which coretime is crucial and downtime is unacceptable. Those chains will be willing to pay a premium to get guaranteed continuous coretime: They ideally don't want any risk of getting front run and outbid. What they also want is being able to plan ahead and know for the upcoming year what they are going to pay for coretime (the most).
The third and final requirement is that we want the market to be stable. In particular price manipulation attacks should not be (easily) possible. In particular they must not be amplified.
Sales
Coretime is organized around 28 day regions. In the first 28 days' region there is a sale happening for the coretime of the next 28 day region and so forth. So while the cores of the previous sale are served, the sale of the next region is ongoing. The sale over that 28 day period is organized in phases.
RFC-1 specifies a basic model for the price function, consisting of three phases:
- The interlude phase
- The leadin period
- Stable minimum price at the end
A most basic implementation would be linear and look something like this:
Now let's look into these phases.
Interlude Phase (Renewals)
This phase is interesting and allows us to fulfill requirement (2): Providing a way to offer guaranteed coretime to heavy duty blockchains.
In this phase only renewals are allowed, the regular sale has not yet started, so you can not be outbid or front run. So what exactly is a renewal? For heavy duty blockchains, which actually absolutely need a block every 6s, they can ask for a renewal of their coretime, assuming they assigned their previously purchased whole 28 days region to one parachain, unshared.
So how does it work in practice?
First let's clarify renewals a bit, as they are actually a bit involved. Usually you will at some point (ignoring legacy auction leases for now) have
purchased coretime via the market we are describing in this very blog post. The price you paid there will be recorded and you are eligible for renewal in the next period, using that same price. Now this renewal will then in turn also already predetermine the price you will need to pay for the next: It is the minimum of the price you paid for this one + renewal bump
(e.g. 3%) and the market price (e.g. in the interlude phase - only phase where renewal is guaranteed, the maximum price). So TL;DR: The price you pay in a renewal is always predetermined one period ahead, which is meant to help with price predictability.
Therefore, if you do your renew
call in the interlude, you get a guarantee on your core and you can calculate an upper bound on the cost.
This fulfills all the requirements. The issue is, is this "fair" and does it conflict with other requirements? One goal of Agile Coretime was to maximize resource utilization. Occupying a core fully, if most of your blocks are empty is not a good use of the validator's time. Therefore the renewal system should only be attractive to chains which really need consistent block times every 6 seconds. Luckily the described system achieves that:
As mentioned, if you want a guarantee for your renewal, you have to do it in the interlude phase, but this also implies that the price you are paying for the coretime will keep going up by renewal bump
until it hits the upper bound of the current market, where it is capped. Therefore at least over time, you will pay more for your coretime than people competing on the open market. Hence if you can afford some risk, you will move back to the open market again and maybe even consider secondary markets, where you can buy coretime every 12s or even less frequent, if that fulfills your need equally. On top of that there is also on-demand coretime.
Leadin Phase
In that phase we go down from the maximum price of the interlude down to the minimum price (configured and adjusted over time). In the simplest form this is simply linearly going down over e.g. 2 weeks. This is the Dutch auction part and people can buy the still available cores whenever the price appears low enough to them.
Stable Minimum
The last section is whatever is left of the 28 days period. Here the price just stays stable at the minimum and any last buyers can still buy cores, in case they were not sold out before.
Price Adjustments
Now with the general explanations out of the way, let's dive deeper into how things actually work and where things are becoming problematic.
- How should the leadin curve actually look? Is linear a good fit? And if so, how big should the factor be: Like how much larger than the minimum price should the interlude price be?
- How do we determine and adjust the minimum price from period to period?
The original proposal of RFC-1 suggested a linear model, with the minimum price being the actually expected sale price (more or less). Performance of the market would be measured with how many cores we intended to sell, vs. how many we actually sold. If we sold all cores we would adjust the minimum price to the sell out price + some additional percentage.
This model was adjusted and tuned and deployed on Kusama and it immediately proved to be problematic. Let's assume for example (similar to what actually happened on Kusama) there was only a single core for sale and that one was sold out right at the beginning of the leadin period. This would have resulted in the next minimum price to be that high price + some percentage.
This was causing some conflicts, because on the one hand you would want a high leadin factor to better capture the right price (otherwise you have to guess really well), but the higher the leadin the higher the next minimum price might go - it would overcompensate. In the example above, a single person willing to pay e.g. 10x, would have driven the minimum price to at least those 10x for the next sale, which now might have 10 cores or more for sale: Price manipulation was being amplified!
Proposed New Curve and Adjustments
Therefore we needed something better and I decided to change the broker pallet to no longer adjust based on cores sold, but rather on the achieved price directly and made sure price manipulation attacks would no longer be possible. Full details on this ticket, actual code here.
Let's start with the curve. First by changing how price adjustments work, we are now able to use really high leadin factors: We are going with 100 for now. Furthermore we define a target price, which is no longer the minimum, but in the middle of the leadin. Meaning our target price is ten times the minimum price and the interlude price is ten times the target price or 100 times the minimum price. We therefore allow the target price to be off in both directions of a factor of 10 and would still be able to capture the value correctly.
We no longer have a plain linear curve, but instead are approximating an exponential model to have the target price in the middle of the time period as well:
Now, how do price adjustments work? Quite straight forward: We define the next target price as the sellout price of the previous sale. If the last sold core was sold at 10 DOT for example, we would configure the minimum price of the next sale to be 1 DOT.
Now have we resolved our issue with price manipulation? Yes! Let's assume the minimum was 1 DOT, the maximum therefore 100 DOT. Someone bought that one core that was for sale for that 100 DOT. In the next sale period the minimum price would still only be 10 DOT, which was the target price of the previous sale. We therefore achieved two things here:
- The worst thing that can happen from one sale to the next is that the new minimum price is the old target price. Meaning, even in that extreme scenario people get cores for the price that was seen as the "market price" anyways just one sale before.
- There is no amplification anymore. That 100 DOT sale, only brought up the minimum price to 10 DOT. That is still 10 times cheaper than the investment.
Renewal Price
To make the model work even in case of renewals, I had to made adjustments to what we consider the sellout price. In RFC-1 renewals would not affect the sellout price, this is now no longer the case and the renewals affect the price just as normal purchases.
With this, even a market only consisting of renewals still has a market price, ensuring the open market stays aligned with renewals. On the flip side if a renewal is made in the leadin, it is competing with the regular market and could be outbid anytime, we therefore also record the price that was paid for the renewal as the sellout price (if it was the last purchase).
For a normal market where renewals are done in the interlude and there are still some cores left for sale, the taking into account of renewals will have no effect whatsoever as the sellout price used is the last price that was achieved.
Renewal Price for Expired Leases
We covered in the "Interlude Phase" how renewals work for cores that have been bought with coretime. There is another way to be allowed for renewals and that is if you have been a lease holder in the now obsolete auction model. In that case your first renewal price will be determined by the target price of the sale period where your lease expires.
Further (future) Alternatives
The proposed curve is already hinting into a direction that has been suggested as an alternative by a few people: Instead of having a linear curve or a linear curve with a dent in the middle, we could go all the way and create an exponential leadin curve. An advantage of an exponential leadin would be that price adjustments of the minimum price would likely not be needed at all, except if the price ever moved into the very steep part of the curve, then we might run into problems with price changes being too high from block to block.
Such a drift, if a problem at all, would only happen very slowly though and thus could easily be addressed via governance. I personally would therefore consider a full exponential curve a valid alternative solution and should definitely be reconsidered if the here proposed solution is still showing any problems in practice.
Polkadot Blockchain Dev
1 comment