What’s Expected of a FIO Block Producer? Part 1: Maintenance
Early in the design of the FIO Protocol a strategic decision was made, that many of the state cleanup tasks that could be done automatically should instead be done by the block producers. It was reasoned, in order to claim tokens, they are already running some script on a regular basis so adding a few extra transactions in this script wouldn’t be onerous. The wisdom of this choice was reinforced later when we learned that there are dangers in using `on_block` to trigger actions — like a situation where a failed action can prevent adding transactions to a block and preventing on-chain finality. No finality? Dead chain. Luckily these lessons were learned on testnet.
One of the least understood, and possibly least communicated responsibilities of a block producer on the FIO network is to handle some of these maintenance actions. These fall into roughly three categories:
- Actions that cleanup state
- Actions that trigger calculations and payouts
- Protecting the utility of the FIO token by ensuring that on-chain fees are reasonable
Of course this information is in the documentation on the knowledge base and developer hub docs, but I’m not sure it’s all in one place and the importance explained. So I’ll cover it in a little more detail.
There are five actions I’d expect to see all block producers calling on a regular basis. These are:
The fio.fee::setfeevote action deserves a special mention bringing the number to six important maintenance actions. It should only be necessary when new fees are added, not on a regular basis throughout the day.
One quick note on the burnexpired, bpclaim, tpidclaim, and computefees actions, if there is no work to be done they will return an error. It’s safe to ignore these errors, throw em at the chain and if they stick, great, if not no problem.
Let’s work top to bottom, the first call on the list `fio.address::burnexpired` won’t do anything at the time I’m writing this, but, in just a few weeks it will be relevant on testnet, and a couple of months will be so on mainnet too.
What it does:
Removes a few expired addresses or expired domains from the fio.address tables. (I think it’s around ten rows, but don’t quote me on that.)
FIO names and domains are NFTs with an ephemeral nature, they require annual renewals, and without being renewed eventually expire. Everything in a table on an EOSIO chain is part of the chain’s “state”. State is locked 1:1 in RAM on a node. So leaving name and domain registrations in state after they have expired offers no benefit, and consumes memory. Today (Feb. 2nd, 2021) mainnet state is only 222 MB, so not entirely a pressing issue but smaller tables benefit in more ways than just reducing the RAM required for a dedicated node.
What it does:
Because running hardware and skilled people are not free! There’s one aspect to bpclaim that is a little different on FIO than other EOSIO chains, and here’s why I suggest calling bpclaim more frequently than rewards are paid: This call, like other maintenance calls triggers some additional calculations on the backend, these update various pools (such as the long-term bp buckets, and the immediate payout pool.) Updating these assists with calculating the circulating supply on-chain. Tokens in the pay buckets or pools are not considered as in-circulation.
Technology Provider Claims
What it does:
Pays rewards to technology providers (wallets, exchanges, domain sale sites, etc.) Any call on-chain, that collects a fee and has a `tpid` field in the action, generates a reward paid to the owner of the FIO name in the `tpid` field.
The technology provider features in FIO incentivize integration with the protocol, by bringing users into the network an entity can be rewarded. But why should a producer call this along with the other maintenance calls? Partly to be polite and help out the technology providers, not all of the technology partners know to call this action. Much like several of the other maintenance calls, tpidclaim only processes a few records/payouts at a time, so even if one provider is regularly calling the action not all payouts may be occurring. The action also affects the calculation of circulating supply by updating several tables.
Fee Votes, Multipliers, and Computing Fees (the big one!)
What it does:
These calls adjust fees on the FIO chain.
If the FIO token price fluctuates it can result in prices that don’t reflect the utility of purchasing an address. If the token price increases, and the number of tokens required to purchase a domain does not decrease it will create a dis-incintive for users to actively use the protocol. These mechanisms allow for adjusting the real cost of using the protocol. Right now I believe most producers are targeting around a two dollar equivalent for registering a FIO name.
Fees deserve a little more explanation, there are three phases in the process of updating the fees:
- The producers vote for the “base” fees using `fio.fee::setfeevote`. The amount charged for each action is up to the producer, but my personal suggestion is that the base fees from genesis (or in most cases the suggested fee when a new action is added) should be used. *There is one exception where I believe the fee was set too high when the action was introduced `setfeemult` really should be cheaper than it is.* Given the current cost of this action, it discourages producers from setting the fee multiplier (and at present as a standby producer, the cost of calling setfeemult several times a day actually exceeds the daily rewards.)
- The second part is setting the multiplier. This is a little more complex, and in a sense requires the producer to act as a price oracle of sorts. This multiplier is applied to the fee, and is actually how a producers fee vote is determined. This is the most important step, and potentially perilous. I don’t want to make any specific recommendations on how a producer should come to this number, but I would suggest a few recommendations: don’t update too often (hourly is plenty, I only update every 4 hours,) ensure whatever price feed being consumed doesn’t give weird numbers (anything more than a few percent adjustment and my oracle ignores the price feed,) and finally don’t update for very small changes, it’s a waste of fees and requires more calls to computefees (see below,) and finally a little variability in the exact moment the fees are set can help prevent price manipulation via flash attacks and other attempts at manipulation.
- After the first two steps, the producer has only submitted their vote, but no updates have actually occurred until computefees is executed. This call, much like burnexpired, works in small chunks, only calculating a few of the fees at a time. So it’s likely that it needs to be called several times.
The developer documentation does a good job of explaining how specifically to handle setting fees. That’s a lot to consider, but maintaining fees are one of the most important and complex jobs on the FIO Protocol and this job falls solely to the block producers.
Here’s the process I use that covers all of the above scenarios:
Every 4 hours I run a program that:
- Immediately calls bpclaim and tpidclaim
- Pulls pricing information for multiple exchanges, and averages the token price.
- If it’s a very small or a large change, it skips voting.
- Waits for a random period, up to 10 minutes to reduce predictability, then submits the multiplier vote.
- At the end of each run, it calls computefees up to three times waiting a second between calls.
- Finally I throw in a burnexpired call for good measure.
It’s a lot of information, but also important for the health of the protocol that these maintenance steps are performed. This is one part of the criteria I use in my automated voting proxy that ranks producers based on measurable on-chain activity and votes for those who are most actively participate in the ongoing health of the chain.
For more information about Block Pane, LLC. please visit blockpane.com.