Ante Tech Talk #3: Fixed-point Arithmetic and Rounding in AntePool

Ante
Ante Labs
Published in
4 min readFeb 4, 2022

This continues a multi-part series walking through the Ante v0.5 protocol design as well as basic secure smart contract development practices. Check out Part 1 and Part 2.

Today’s post covers an idiosyncrasy arising from the fact that all variables in the EVM are represented as integers rather than floats. This means that we must use integer arithmetic to simulate computations with real numbers. Though this problem also arises in “ordinary” programming, the adversarial nature of public blockchains requires extra care.

Numerical variables in EVM and smart contracts

On any computer, data is stored using discrete bits, but real numbers are usually represented up to some level of precision using floating-point arithmetic. When dealing with financial data, however, it is often advisable to use fixed-point arithmetic instead to avoid possible issues with rounding or cross-platform reproducibility.

When writing smart contracts or writing on the EVM, these difficulties are compounded. First, gas costs are assessed on operations with 256-bit word size, meaning that floating-point operations incur additional gas costs. In addition, they are not natively included in Solidity, though it is possible to implement them.

Second, smart contract programming often occurs in an adversarial environment. In such a setting, seemingly small rounding errors which would ordinarily be immaterial can compound and potentially be economically exploited by an attacker. Because we are dealing with variables which directly determine ownership of tokens or money, we must ensure that the choices of rounding used in fixed-point arithmetic do not impact protocol security.

Fixed-point arithmetic and decay math in AntePool

AntePool represents numerical constants such as the decay rate using fixed-point arithmetic with scaling factor 1e18. In this post, we will focus on the usage of fixed-point arithmetic on the challenger side of the pool.

Recall from Part 2 that information about challengers is stored in the challengerInfo variable. Specifically, the total amount of ETH owned by challengers is given by challengerInfo.totalAmount, and the challenger balance of a given address addr is computed in getStoredBalance()by simulating a call of updateDecay() and then rounding the unrounded challenger balance:

Although the total balance would naturally be equal to the sum of each users’ balance if everything is computed to infinite precision, in practice the rounding involved in fixed-point arithmetic will make them unequal. Most importantly, we want to make sure that no sequence of operations can cause the sum of challenger balances to exceed the total amount of ETH owned by challengers, as this would make the AntePool insolvent! That is, we want to ensure that:

Secondly, any additional ETH in the total challenger side balance which is not attributed to any user is simply locked in the AntePool. We would like this amount to not grow too quickly. We call this quantity the challenger excess balance, with a similar definition for the staker side as well:

Empirically characterizing the excess balance

To get an initial sense for how the excess balance behaves, we used a randomized fuzz test to track it across a sequence of staking, challenging, and unstaking operations on a simulated blockchain. Specifically, we mocked a local testnet blockchain with Hardhat, deployed a single AntePool, and ran 100 test iterations consisting of:

  • from each of 10 test addresses, staking or challenging the pool with a random amount of ETH
  • advancing the chain for a random number of blocks
  • from each of 10 test addresses, unstaking from either the staker or challenger pool with a random amount of ETH
  • advancing the chain for a random number of blocks

After each test iteration, we recorded the excess balance for both the staker and challenger pools, plotted in blue below.

Excess balances accrued over time in a single run of our fuzz test

As the figure shows, in both staker and challenger pools, the excess balances are non-negative and stay extremely small. In fact, they remain on the order of 1,000s of wei even after 100 staking and unstaking iterations, which is an acceptable level for any practical usage.

However, this outcome is from just a single run of our fuzz test. Though we get similar results from more runs, how can we be sure that the excess balances always behave well? To get a greater level of certainty, in Part 4 of this series, we will analyze the situation theoretically and obtain provable upper bounds on the excess balances (shown in orange in the previous figure) which ensure they do not grow too quickly.

Conclusion

To try out Ante for yourself, stake or challenge Ante Tests on Ante v0.5 live on Mainnet now. If you’d like to get more involved, you can now write your own Ante Tests or ask your favorite protocols to write and stake their own Ante Tests!

Let us know if you have any questions/comments, follow @AnteFinance on Twitter, and join our Discord for more discussion!

--

--

Ante
Ante Labs

Building a Smart Tests Community for @AnteFinance