Unity Staking Contracts Overview

Aion
Aion
Published in
7 min readSep 3, 2019

At the heart of the Unity Consensus are the staking and stake delegation mechanisms, which are responsible for managing staker registrations and stake updates. In this article, I will walk you through the design considerations we’ve made and the implementation details. The source code is available at https://github.com/aionnetwork/protocol_contracts.

NOTE: This article is an overview of the current state of the Unity Protocol Contracts, which are still in their infancy stage and are constantly evolving.

Design Considerations

Any Proof-of-Stake(PoS) based system needs a way of managing the stake state, i.e. the registered staker list and the amount of stake of each staker at any time. This piece of information decides the eligibility of a staker to produce a PoS block, and has to be agreed on by the whole network.

The stake management is traditionally implemented within the client (software that a node uses to connect to the network), using purpose-specific transaction types, with the utilization of databases or data storages. This is mainly due to the lack of programming capability above the protocol. The consequence of such a design is to make the protocol itself heavy and fat. As a result, it’s very difficult to implement such a system, given that there might be multiple clients in different programming languages.

In the Unity Consensus, we implement the staking and stake delegation mechanisms in AVM smart contracts. Such a design choice gives us two advantages:

  • The execution and storage model of a smart contract is well defined, so it will be cost-free to ensure consensus on the stake state;
  • The user interaction will be simple, as a user can interact with a smart contract using existing transaction infrastructure (no extra tool is required).

Staker Registry Contract

First, let’s take a look at the staker registry contract, which is the endpoint to all users for participating in staking. A staker needs to be registered in this contract before it can produce a valid PoS block. A coin-holder can vote/unvote for a registered staker and even transfer stake between two stakers.

The internal structure of the staker registry is shown in the picture below. One staker registry is associated with a collection of registered stakers, pending unvotes and pending transfers. To understand the state/lifecycle of coins, I refer readers to the Section 2.4 of the Unity Engineering Specs. A copy of the coin state transition diagram is also attached below.

Structure of the staker registry contract
Coin states and state transitions from the perspective of the staker registry

The staker registry contract is privileged, in a sense that the kernel depends on it for deciding the validity of a PoS block. Despite being privileged, this contract is designed to be simple and the kernel-contract interaction is limited to just one stateless method call (a stateless method call doesn’t change the state of a smart contract during execution). This method is getEffectiveStake(signingAddress, coinbaseAddress), which returns the effective stake of a block producer.

Register A Staker

Anyone can register a staker by calling the registerStaker method of the staker registry, which has the following parameters.

  • identityAddress — (unique) An address used to identify a staker;
  • managementAddress — The address of the management key used when updating the staker, e.g. updating signing address;
  • signingAddress — (unique) The address of the key used for signing a PoS block;
  • coinbaseAddress — The address used for receiving block rewards;
  • selfBondAddress — The address used for calculating the self-bond stake of a staker (self-bond stake of a staker is the stake from its owner).

Separating the stake of staker owner from the stake of other voters gives us additional benefits and flexibility. For example, we can then apply self-bond percentage requirement and min self-bond requirement.

Vote

Voting is the process of converting liquid coins into stake. To vote for a staker, a coin-holder needs to call the vote(staker) method, with a specific amount of coins being passed along. The coins will be in the custody of the staker registry contract after voting.

Unvote

To revoke a stake and retrieve the coins, one needs to call the unvote(staker, amount) method, which will cancel the stake, lock it for a pre-defined period of time, i.e.UNVOTE_LOCK_UP_PERIOD, and return a unique PendingUnvote ID, if the request is valid. It’s only after the lock-up period that the user can really get its coins back, by calling the finalizeUnvote(id)method.

Transfer Stake

The staker registry also allows a voter to transfer stake from one staker to another. This allows voters to swap stake between block producers without going through an unvote-and-vote process, which would involve the unvote lockup period.

To transfer stake, one needs to call the transferStake(from, to, amount)method, which returns a PendingTransfer ID upon valid request. The user needs to call the finalizeTransfer(id) method to complete the stake transfer, after another lock-up period, i.e. TRANSFER_LOCK_UP_PERIOD.

Pool Registry Contract

While the staker registry is easy to interact with, many coin-holders would prefer to delegate their stake to a pool rather than to maintain a node and stake on their own. We call this group of people delegators. A pool receives stake from and re-distribute the block rewards to its delegators.

In the Unity Consensus, a decentralized stake delegation system, as in PoolRegistry smart contract, is designed and implemented. This is made possible by the following conditions:

  • The staker registry is a smart contract which allows the pool registry to interact with via the inter-contract CALL;
  • An efficient reward distribution algorithm (F1 fee distribution) is proposed by Dev Ojha.

Unlike staker registry, the pool registry is un-privileged and implemented purely based on the existing framework provided by AVM. An architectural view of the design is shown below.

Pool Registry Architecture

At the very front is the PoolRegistry, which is the endpoint for all pool owners and delegators. For each registered pool, the registry maintains a PoolState , which is associated with one PoolCoinbase, one PoolCustodianand one PoolRewardsManager.

  • The PoolCoinbase is smart contract instance, in the control of the pool registry, whose only purpose is to receive block rewards;
  • The PoolCustodian is another smart contract instance, in the control of the pool registry, which is the custodian of the pool owner’s stake;
  • The PoolRewardsManager is an internal object, which calculates the share of rewards of each delegator to a pool.

Register A Pool

To register a pool, the owner needs to go through the following steps:

  1. Publish the meta data, e.g. name and description, in a JSON file at a publicly accessible URL (the scheme of the JSON file is being actively defined).
  2. Call the registerPool(signingAddress, commissionRate, metaDataUrl, metaDataContentHash) method of the pool registry, where signingAddressis the address of the signing key to be used for block production, comissionRate is the service charge in percentage that the pool owner wants to take, and metaDataUrl and metaDataContentHash are the URL and hash of the meta data respectively.

After these two steps, the pool can start accepting stakes from delegators, including the pool owner itself. As long as the min self-bond stake requirement is satisfied, the pool owner can use the signing key to produce blocks and earn block rewards.

An interesting implementation detail is that the registerPool method is convoluted. It creates a staker in the staker registry, instantiates a PoolCoinbase and aPoolCustodian, and configures the staker properly. This will ensure that all block rewards of the pool go to the pool registry.

Delegate

To delegate stake to a pool, one just needs to call the delegate(pool)method along with some amount of AION coins to stake. The pool registry uses the received coin to stake in the staker registry.

Undelegate

Two steps are required to cancel a delegation. First, the delegator needs to call the undelegate(pool, amount) method of the pool registry, which will initiate an unvote operation and return a PendingUnvote ID. Second, it has to call the finalizeUnvote(id) method to finalize it after the unvote lock-up period.

Withdraw

Withdrawal is the process of claiming back the block rewards one delegator has earned so far. This is being done by calling the withdraw(pool) method, which will transfer the rewards to the delegator upon a valid request. For how the contract decides the block rewards, check the F1 fee distribution scheme.

Redelegation and Auto-redelegation

Redelegation is the process of delegating to a pool using the earned block rewards from that pool. This is essentially a combination of withdraw and delegate.

Auto-redelegation enables one delegator to allow others to redelegate its block rewards. This is implemented by introducing an auto-redelegation market. If a delegator sets an auto-redelegation rate, then anyone can call the pool registry to auto-redelegate the delegator’s rewards on its behalf. The caller will get a portion of the rewards as incentives based on the auto-redelegation rate.

How Do Coins Flow?

In case someone is interested in how the coins flow in the delegation system, an overview is provided below (boxes with shadow background are smart contract instances). Intuitively, all delegator’s stake and PoS block rewards are in the custody of the Pool Registry contract; all pool owner’s stake is in the custody of the Pool Custodian contract. If the coins are for delegation, then they flow to the Staker Registry.

Flows of coins in the delegation system

That’s It!

Now, you’ve gone through an overview of the Unity staking and stake delegation contracts. If you want to learn more about the details, please read the Unity Engineer Spec or the smart contract source code.

--

--

Aion
Aion
Editor for

The blog for the digital asset of The Open Application Network