Skip to content

Commit 40f8a23

Browse files
committed
rewards: _pow function can revert in extreme conditions, ensure we test this function under normal boundaries
1 parent 06cd421 commit 40f8a23

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

.soliumignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ contracts/discovery/erc1056
55
contracts/token/IGraphToken.sol
66
contracts/upgrades/GraphProxy.sol
77
contracts/rewards/RewardsManager.sol
8+
contracts/tests/RewardsManagerMock.sol
89
contracts/tests/ens
910
contracts/curation/IGraphCurationToken.sol
1011
contracts/staking/libs/LibFixedMath.sol
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
pragma solidity ^0.6.12;
2+
pragma experimental ABIEncoderV2;
3+
4+
// Mock contract used for testing rewards
5+
contract RewardsManagerMock {
6+
/**
7+
* @dev Raises x to the power of n with scaling factor of base.
8+
* Based on: https://github.com/makerdao/dss/blob/master/src/pot.sol#L81
9+
* @param x Base of the exponentation
10+
* @param n Exponent
11+
* @param base Scaling factor
12+
* @return z Exponential of n with base x
13+
*/
14+
function pow(
15+
uint256 x,
16+
uint256 n,
17+
uint256 base
18+
) public pure returns (uint256 z) {
19+
assembly {
20+
switch x
21+
case 0 {
22+
switch n
23+
case 0 {
24+
z := base
25+
}
26+
default {
27+
z := 0
28+
}
29+
}
30+
default {
31+
switch mod(n, 2)
32+
case 0 {
33+
z := base
34+
}
35+
default {
36+
z := x
37+
}
38+
let half := div(base, 2) // for rounding.
39+
for {
40+
n := div(n, 2)
41+
} n {
42+
n := div(n, 2)
43+
} {
44+
let xx := mul(x, x)
45+
if iszero(eq(div(xx, x), x)) {
46+
revert(0, 0)
47+
}
48+
let xxRound := add(xx, half)
49+
if lt(xxRound, xx) {
50+
revert(0, 0)
51+
}
52+
x := div(xxRound, base)
53+
if mod(n, 2) {
54+
let zx := mul(z, x)
55+
if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) {
56+
revert(0, 0)
57+
}
58+
let zxRound := add(zx, half)
59+
if lt(zxRound, zx) {
60+
revert(0, 0)
61+
}
62+
z := div(zxRound, base)
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}

test/rewards/rewards.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import { expect } from 'chai'
22
import { constants, BigNumber } from 'ethers'
33
import { BigNumber as BN } from 'bignumber.js'
44

5+
import { deployContract } from '../lib/deployment'
56
import { NetworkFixture } from '../lib/fixtures'
67

78
import { Curation } from '../../build/typechain/contracts/Curation'
89
import { EpochManager } from '../../build/typechain/contracts/EpochManager'
910
import { GraphToken } from '../../build/typechain/contracts/GraphToken'
1011
import { RewardsManager } from '../../build/typechain/contracts/RewardsManager'
12+
import { RewardsManagerMock } from '../../build/typechain/contracts/RewardsManagerMock'
1113
import { Staking } from '../../build/typechain/contracts/Staking'
1214

1315
import {
@@ -45,6 +47,7 @@ describe('Rewards', () => {
4547
let epochManager: EpochManager
4648
let staking: Staking
4749
let rewardsManager: RewardsManager
50+
let rewardsManagerMock: RewardsManagerMock
4851

4952
const subgraphDeploymentID1 = randomHexBytes()
5053
const subgraphDeploymentID2 = randomHexBytes()
@@ -128,6 +131,11 @@ describe('Rewards', () => {
128131
governor.signer,
129132
))
130133

134+
rewardsManagerMock = ((await deployContract(
135+
'RewardsManagerMock',
136+
governor.signer,
137+
)) as unknown) as RewardsManagerMock
138+
131139
// 5% minute rate (4 blocks)
132140
await rewardsManager.connect(governor.signer).setIssuanceRate(ISSUANCE_RATE_PER_BLOCK)
133141

@@ -606,4 +614,16 @@ describe('Rewards', () => {
606614
.withArgs(indexer1.address, allocationID, await epochManager.currentEpoch())
607615
})
608616
})
617+
618+
describe('pow', function () {
619+
it('exponentiation works under normal boundaries (annual rate from 1% to 700%, 90 days period)', async function () {
620+
const baseRatio = toGRT('0.000000004641377923') // 1% annual rate
621+
const timePeriods = (60 * 60 * 24 * 10) / 15 // 90 days in blocks
622+
for (let i = 0; i < 50; i = i + 4) {
623+
const r = baseRatio.mul(i * 4).add(toGRT('1'))
624+
const h = await rewardsManagerMock.pow(r, timePeriods, toGRT('1'))
625+
console.log('\tr:', formatGRT(r), '=> c:', formatGRT(h))
626+
}
627+
})
628+
})
609629
})

0 commit comments

Comments
 (0)