diff --git a/packages/protocol/contracts/governance/DowntimeSlasher.sol b/packages/protocol/contracts/governance/DowntimeSlasher.sol index ed37cee69f9..ce2ea171dcd 100644 --- a/packages/protocol/contracts/governance/DowntimeSlasher.sol +++ b/packages/protocol/contracts/governance/DowntimeSlasher.sol @@ -83,7 +83,9 @@ contract DowntimeSlasher is SlasherUtil { uint256 signerIndex = epochNumberOfBlock(n, sz) == startEpoch ? startSignerIndex : endSignerIndex; - if (uint256(getParentSealBitmap(n)) & (1 << signerIndex) != 0) return false; + // We want to check signers for block n, + // so we get the parent seal bitmap for the next block + if (uint256(getParentSealBitmap(n + 1)) & (1 << signerIndex) != 0) return false; } return true; } diff --git a/packages/protocol/test/governance/downtime_slasher.ts b/packages/protocol/test/governance/downtime_slasher.ts index d5c3128356a..2bf440c63f0 100644 --- a/packages/protocol/test/governance/downtime_slasher.ts +++ b/packages/protocol/test/governance/downtime_slasher.ts @@ -1,5 +1,5 @@ import { CeloContractName } from '@celo/protocol/lib/registry-utils' -import { assertContainSubset, assertRevert } from '@celo/protocol/lib/test-utils' +import { assertContainSubset, assertRevert, jsonRpc } from '@celo/protocol/lib/test-utils' import BigNumber from 'bignumber.js' import { AccountsContract, @@ -37,7 +37,7 @@ contract('DowntimeSlasher', (accounts: string[]) => { const slashingPenalty = 10000 const slashingReward = 100 - const slashableDowntime = 3 + const slashableDowntime = 10 beforeEach(async () => { accountsInstance = await Accounts.new() @@ -70,7 +70,7 @@ contract('DowntimeSlasher', (accounts: string[]) => { }) it('should have set slashable downtime', async () => { const res = await slasher.slashableDowntime() - assert.equal(res.toNumber(), 3) + assert.equal(res.toNumber(), slashableDowntime) }) it('can only be called once', async () => { await assertRevert(slasher.initialize(registry.address, 10000, 100, 2)) @@ -132,28 +132,54 @@ contract('DowntimeSlasher', (accounts: string[]) => { describe('#slash()', () => { let blockNumber: number let startBlock: number + let changeBlock: number const validatorIndex = 0 + before(async () => { + let bn: number = 0 + do { + bn = await web3.eth.getBlockNumber() + await jsonRpc(web3, 'evm_mine', []) + } while (bn < 350) + }) beforeEach(async () => { blockNumber = await web3.eth.getBlockNumber() startBlock = blockNumber - 50 const epoch = (await slasher.getEpochNumberOfBlock(blockNumber)).toNumber() await slasher.setEpochSigner(epoch, validatorIndex, validator) await slasher.setEpochSigner(epoch - 1, validatorIndex, validator) + await slasher.setEpochSigner(epoch - 2, 1, validator) await slasher.setEpochSigner(epoch + 1, validatorIndex, validator) // Signed by validators 0 and 1 const bitmap = '0x0000000000000000000000000000000000000000000000000000000000000003' // Signed by validator 1 const bitmap2 = '0x0000000000000000000000000000000000000000000000000000000000000002' + // Signed by validator 0 + const bitmap3 = '0x0000000000000000000000000000000000000000000000000000000000000001' await slasher.setParentSealBitmap(blockNumber, bitmap) await slasher.setParentSealBitmap(blockNumber + 1, bitmap) await slasher.setParentSealBitmap(blockNumber + 2, bitmap2) await slasher.setNumberValidators(2) + // when epoch-2 changes to epoch-1, the validator to be slashed is down, but our signer number changes + // another validator is up around the epoch change + changeBlock = (epoch - 1) * 100 - 3 + async function prepareBlock(bn) { + const parentEpoch = (await slasher.getEpochNumberOfBlock(bn - 1)).toNumber() + await slasher.setParentSealBitmap(bn, parentEpoch === epoch - 2 ? bitmap3 : bitmap2) + } + for (let i = 0; i < 7; i++) { + await prepareBlock(changeBlock + i) + } }) it('fails if they were signed', async () => { await assertRevert( slasher.slash(blockNumber, validatorIndex, validatorIndex, 0, [], [], [], [], [], []) ) }) + it('success with validator index change', async () => { + await slasher.slash(changeBlock, 1, validatorIndex, 0, [], [], [], [], [], []) + const balance = await mockLockedGold.accountTotalLockedGold(validator) + assert.equal(balance.toNumber(), 40000) + }) it('decrements gold when success', async () => { await slasher.slash(startBlock, validatorIndex, validatorIndex, 0, [], [], [], [], [], []) const balance = await mockLockedGold.accountTotalLockedGold(validator)