Skip to content

Commit

Permalink
Merge pull request lidofinance#364 from lidofinance/happy_path_rework
Browse files Browse the repository at this point in the history
Happy path rework
  • Loading branch information
Psirex authored Oct 14, 2021
2 parents ea025dd + 1670559 commit 66e9c67
Show file tree
Hide file tree
Showing 7 changed files with 563 additions and 43 deletions.
9 changes: 9 additions & 0 deletions contracts/0.4.24/test_helpers/DepositContractMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract DepositContractMock is IDepositContract {
}

Call[] public calls;
bytes32 internal depositRoot;

function deposit(
bytes /* 48 */ pubkey,
Expand All @@ -40,4 +41,12 @@ contract DepositContractMock is IDepositContract {
function reset() external {
calls.length = 0;
}

function get_deposit_root() external view returns (bytes32) {
return depositRoot;
}

function set_deposit_root(bytes32 _newRoot) external {
depositRoot = _newRoot;
}
}
12 changes: 12 additions & 0 deletions test/helpers/blockchain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
async function waitBlocks(numBlocksToMine) {
let block
for (let i = 0; i < numBlocksToMine; ++i) {
await network.provider.send('evm_mine')
block = await web3.eth.getBlock('latest')
}
return block
}

module.exports = {
waitBlocks
}
39 changes: 36 additions & 3 deletions test/scenario/helpers/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,27 @@ const Lido = artifacts.require('LidoMock.sol')
const NodeOperatorsRegistry = artifacts.require('NodeOperatorsRegistry')
const OracleMock = artifacts.require('OracleMock.sol')
const DepositContractMock = artifacts.require('DepositContractMock.sol')
const DepositSecurityModule = artifacts.require('DepositSecurityModule.sol')

module.exports = {
deployDaoAndPool
}

async function deployDaoAndPool(appManager, voting, depositor) {
const NETWORK_ID = 1000
const MAX_DEPOSITS_PER_BLOCK = 100
const MIN_DEPOSIT_BLOCK_DISTANCE = 20
const PAUSE_INTENT_VALIDITY_PERIOD_BLOCKS = 10
const GUARDIAN1 = '0x5Fc0E75BF6502009943590492B02A1d08EAc9C43'
const GUARDIAN2 = '0x8516Cbb5ABe73D775bfc0d21Af226e229F7181A3'
const GUARDIAN3 = '0xdaEAd0E0194abd565d28c1013399801d79627c14'
const GUARDIAN_PRIVATE_KEYS = {
[GUARDIAN1]: '0x3578665169e03e05a26bd5c565ffd12c81a1e0df7d0679f8aee4153110a83c8c',
[GUARDIAN2]: '0x88868f0fb667cfe50261bb385be8987e0ce62faee934af33c3026cf65f25f09e',
[GUARDIAN3]: '0x75e6f508b637327debc90962cd38943ddb9cfc1fc4a8572fc5e3d0984e1261de'
}
const DEPOSIT_ROOT = '0xd151867719c94ad8458feaf491809f9bc8096c702a72747403ecaac30c179137'

async function deployDaoAndPool(appManager, voting) {
// Deploy the DAO, oracle and deposit contract mocks, and base contracts for
// Lido (the pool) and NodeOperatorsRegistry (the Node Operators registry)

Expand All @@ -35,6 +50,18 @@ async function deployDaoAndPool(appManager, voting, depositor) {
NodeOperatorsRegistry.at(nodeOperatorRegistryProxyAddress)
])

const depositSecurityModule = await DepositSecurityModule.new(
pool.address,
depositContractMock.address,
nodeOperatorRegistry.address,
NETWORK_ID,
MAX_DEPOSITS_PER_BLOCK,
MIN_DEPOSIT_BLOCK_DISTANCE,
PAUSE_INTENT_VALIDITY_PERIOD_BLOCKS,
{ from: appManager }
)
await depositSecurityModule.addGuardians([GUARDIAN3, GUARDIAN1, GUARDIAN2], 2, { from: appManager })

// Initialize the node operators registry and the pool
await nodeOperatorRegistry.initialize(pool.address)

Expand Down Expand Up @@ -74,7 +101,7 @@ async function deployDaoAndPool(appManager, voting, depositor) {
acl.createPermission(voting, pool.address, POOL_BURN_ROLE, appManager, { from: appManager }),

// Allow depositor to deposit buffered ether
acl.createPermission(depositor, pool.address, DEPOSIT_ROLE, appManager, { from: appManager }),
acl.createPermission(depositSecurityModule.address, pool.address, DEPOSIT_ROLE, appManager, { from: appManager }),

// Allow voting to manage node operators registry
acl.createPermission(voting, nodeOperatorRegistry.address, NODE_OPERATOR_REGISTRY_MANAGE_SIGNING_KEYS, appManager, {
Expand Down Expand Up @@ -104,6 +131,7 @@ async function deployDaoAndPool(appManager, voting, depositor) {

await oracleMock.setPool(pool.address)
await depositContractMock.reset()
await depositContractMock.set_deposit_root(DEPOSIT_ROOT)

const [treasuryAddr, insuranceAddr] = await Promise.all([pool.getTreasury(), pool.getInsuranceFund()])

Expand All @@ -116,6 +144,11 @@ async function deployDaoAndPool(appManager, voting, depositor) {
pool,
nodeOperatorRegistry,
treasuryAddr,
insuranceAddr
insuranceAddr,
depositSecurityModule,
guardians: {
privateKeys: GUARDIAN_PRIVATE_KEYS,
addresses: [GUARDIAN1, GUARDIAN2, GUARDIAN3]
}
}
}
162 changes: 146 additions & 16 deletions test/scenario/lido_deposit_iteration_limit.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const { assert } = require('chai')
const { assertBn, assertRevert } = require('@aragon/contract-helpers-test/src/asserts')
const { getEvents, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test')
const { assertBn } = require('@aragon/contract-helpers-test/src/asserts')
const { getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test')

const { pad, ETH, hexConcat } = require('../helpers/utils')
const { deployDaoAndPool } = require('./helpers/deploy')
const { newDao } = require('@aragon/toolkit/dist/dao')
const { signDepositData } = require('../0.8.9/helpers/signatures')
const { waitBlocks } = require('../helpers/blockchain')

const NodeOperatorsRegistry = artifacts.require('NodeOperatorsRegistry')

Expand All @@ -18,10 +18,8 @@ contract('Lido: deposit loop iteration limit', (addresses) => {
nodeOperator,
// users who deposit Ether to the pool
user1,
user2,
// an unrelated address
nobody,
depositor
nobody
] = addresses

// Limits the number of validators assigned in a single transaction, regardless the amount
Expand All @@ -31,9 +29,10 @@ contract('Lido: deposit loop iteration limit', (addresses) => {
const depositIterationLimit = 5

let pool, nodeOperatorRegistry, depositContractMock
let depositSecurityModule, depositRoot, guardians

it('DAO, node operators registry, token, and pool are deployed and initialized', async () => {
const deployed = await deployDaoAndPool(appManager, voting, depositor)
it('DAO, node operators registry, token, pool and deposit security module are deployed and initialized', async () => {
const deployed = await deployDaoAndPool(appManager, voting)

// contracts/Lido.sol
pool = deployed.pool
Expand All @@ -44,6 +43,10 @@ contract('Lido: deposit loop iteration limit', (addresses) => {
// mocks
depositContractMock = deployed.depositContractMock

depositSecurityModule = deployed.depositSecurityModule
guardians = deployed.guardians
depositRoot = await depositContractMock.get_deposit_root()

await pool.setFee(0.01 * 10000, { from: voting })
await pool.setFeeDistribution(0.3 * 10000, 0.2 * 10000, 0.5 * 10000, { from: voting })
await pool.setWithdrawalCredentials(pad('0x0202', 32), { from: voting })
Expand Down Expand Up @@ -90,23 +93,90 @@ contract('Lido: deposit loop iteration limit', (addresses) => {
assertBn(ether2Stat.depositedValidators, 0, 'deposited validators')
})

it('one can assign the buffered ether to validators by calling depositBufferedEther() and passing deposit iteration limit', async () => {
it('guardians can assign the buffered ether to validators by calling depositBufferedEther() and passing deposit iteration limit', async () => {
const depositIterationLimit = 5
await pool.depositBufferedEther(depositIterationLimit, { from: depositor })
let bufferedEther = await pool.getBufferedEther()
console.log('Buffered Ether:', bufferedEther.toString())

const block = await web3.eth.getBlock('latest')
const keysOpIndex = await nodeOperatorRegistry.getKeysOpIndex()
const signatures = [
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[0]]
),
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[1]]
)
]
await depositSecurityModule.depositBufferedEther(depositIterationLimit, depositRoot, keysOpIndex, block.number, block.hash, signatures)

// no more than depositIterationLimit validators are assigned in a single transaction
assertBn(await depositContractMock.totalCalls(), 5, 'total validators assigned')

const ether2Stat = await pool.getBeaconStat()
assertBn(ether2Stat.depositedValidators, 5, 'deposited validators')

bufferedEther = await pool.getBufferedEther()

// the rest of the received Ether is still buffered in the pool
assertBn(await pool.getBufferedEther(), ETH(15 * 32), 'buffered ether')
})

it('one can advance the deposit loop further by calling depositBufferedEther() once again', async () => {
it('guardians can advance the deposit loop further by calling depositBufferedEther() once again', async () => {
const depositIterationLimit = 10
await pool.depositBufferedEther(depositIterationLimit, { from: depositor })

let block = await waitBlocks(await depositSecurityModule.getMinDepositBlockDistance())
let keysOpIndex = await nodeOperatorRegistry.getKeysOpIndex()
let signatures = [
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[0]]
),
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[1]]
)
]
await depositSecurityModule.depositBufferedEther(depositIterationLimit, depositRoot, keysOpIndex, block.number, block.hash, signatures)

block = await waitBlocks(await depositSecurityModule.getMinDepositBlockDistance())
keysOpIndex = await nodeOperatorRegistry.getKeysOpIndex()
signatures = [
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[0]]
),
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[1]]
)
]

assertBn(await depositContractMock.totalCalls(), 15, 'total validators assigned')

Expand All @@ -118,7 +188,27 @@ contract('Lido: deposit loop iteration limit', (addresses) => {

it('the number of assigned validators is limited by the remaining ether', async () => {
const depositIterationLimit = 10
await pool.depositBufferedEther(depositIterationLimit, { from: depositor })
const block = await waitBlocks(await depositSecurityModule.getMinDepositBlockDistance())
const keysOpIndex = await nodeOperatorRegistry.getKeysOpIndex()
const signatures = [
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[0]]
),
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[1]]
)
]
await depositSecurityModule.depositBufferedEther(depositIterationLimit, depositRoot, keysOpIndex, block.number, block.hash, signatures)

assertBn(await depositContractMock.totalCalls(), 20)

Expand All @@ -139,7 +229,27 @@ contract('Lido: deposit loop iteration limit', (addresses) => {

it('the number of assigned validators is still limited by the number of available validator keys', async () => {
const depositIterationLimit = 10
await pool.depositBufferedEther(depositIterationLimit, { from: depositor })
const block = await waitBlocks(await depositSecurityModule.getMinDepositBlockDistance())
const keysOpIndex = await nodeOperatorRegistry.getKeysOpIndex()
const signatures = [
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[0]]
),
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[1]]
)
]
await depositSecurityModule.depositBufferedEther(depositIterationLimit, depositRoot, keysOpIndex, block.number, block.hash, signatures)

assertBn(await depositContractMock.totalCalls(), 21)

Expand All @@ -152,7 +262,27 @@ contract('Lido: deposit loop iteration limit', (addresses) => {

it('depositBufferedEther is a nop if there are no signing keys available', async () => {
const depositIterationLimit = 10
await pool.depositBufferedEther(depositIterationLimit, { from: depositor })
const block = await waitBlocks(await depositSecurityModule.getMinDepositBlockDistance())
const keysOpIndex = await nodeOperatorRegistry.getKeysOpIndex()
const signatures = [
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[0]]
),
signDepositData(
await depositSecurityModule.ATTEST_MESSAGE_PREFIX(),
depositRoot,
keysOpIndex,
block.number,
block.hash,
guardians.privateKeys[guardians.addresses[1]]
)
]
await depositSecurityModule.depositBufferedEther(depositIterationLimit, depositRoot, keysOpIndex, block.number, block.hash, signatures)

assertBn(await depositContractMock.totalCalls(), 21, 'total validators assigned')

Expand Down
Loading

0 comments on commit 66e9c67

Please sign in to comment.