Skip to content

Commit dc088cf

Browse files
committed
Fix comments and add mock tests for BlockRoots
1 parent 713407b commit dc088cf

File tree

4 files changed

+98
-4
lines changed

4 files changed

+98
-4
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity 0.8.30;
3+
4+
/// @dev Used to mock the "precompile" BeaconRoots contract
5+
contract BeaconRootsMock {
6+
mapping(uint256 => bytes32) internal beaconRoots;
7+
8+
constructor() {}
9+
10+
function setParentBlockRoot(uint256 _timestamp, bytes32 _root) external {
11+
beaconRoots[_timestamp] = _root;
12+
}
13+
14+
fallback(bytes calldata _input) external returns (bytes memory) {
15+
uint256 timestamp = abi.decode(_input, (uint256));
16+
if (beaconRoots[timestamp] != 0) {
17+
return abi.encode(beaconRoots[timestamp]);
18+
}
19+
revert();
20+
}
21+
}

contracts/contract/util/BeaconStateVerifier.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ contract BeaconStateVerifier is RocketBase, BeaconStateVerifierInterface {
4343
historicalSummaryOffset = slotCapella / slotsPerHistoricalRoot;
4444
}
4545

46-
/// @notice Verifies a proof about the pubkey/withdrawal_credentials root of a validator on the beacon chain
46+
/// @notice Verifies a proof about a validator on the beacon chain
4747
function verifyValidator(ValidatorProof calldata _proof) override external view returns(bool) {
4848
// Only support post-electra state proofs
4949
require(_proof.slot >= slotElectra, "Invalid proof");

contracts/contract/util/BlockRoots.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ contract BlockRoots is BlockRootsInterface {
2727
// Make sure the slot is recent enough that it will exist in the beaconRoots contract
2828
uint256 slotTimestamp = getTimestampFromSlot(_slot + 1);
2929
uint256 earliestTimestamp = block.timestamp - (beaconRootsHistoryBufferLength * secondsPerSlot);
30-
require (slotTimestamp > earliestTimestamp);
31-
// Walk backwards from the given timestamp 1 slot at a time until block root is found
30+
require (slotTimestamp > earliestTimestamp, "Slot too old");
31+
// Walk forwards from the given timestamp 1 slot at a time until block root is found
3232
while (slotTimestamp <= block.timestamp) {
3333
(bool success, bytes memory result) = beaconRoots.staticcall(abi.encode(slotTimestamp));
3434
if (success && result.length > 0) {

test/util/verifier-tests.js

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { before, describe, it } from 'mocha';
22
import { printTitle } from '../_utils/formatting';
3-
import { BeaconStateVerifier, BlockRootsMock } from '../_utils/artifacts';
3+
import { artifacts, BeaconStateVerifier, BlockRootsMock } from '../_utils/artifacts';
44
import * as assert from 'assert';
55
import { shouldRevert } from '../_utils/testing';
6+
import { time } from '@nomicfoundation/hardhat-network-helpers';
67

78
const hre = require('hardhat');
89
const ethers = hre.ethers;
@@ -368,5 +369,77 @@ export default function() {
368369

369370
assert.equal(await beaconStateVerifier.verifyWithdrawal(correctProof), true);
370371
});
372+
373+
it(printTitle('BlockRoots', 'Returns the correct block hash'), async () => {
374+
// Choose genesis time 1000 slots ago
375+
const block = await ethers.provider.getBlock();
376+
const beaconGenesisTime = (BigInt(block.timestamp) / 12n * 12n) - (1000n * 12n);
377+
const secondsPerSlot = 12n
378+
379+
const BeaconRootsMock = artifacts.require('BeaconRootsMock');
380+
const BlockRoots = artifacts.require('BlockRoots');
381+
const beaconRootsMock = await BeaconRootsMock.new();
382+
const blockRoots = await BlockRoots.new(beaconGenesisTime, secondsPerSlot, 8191n, beaconRootsMock.target);
383+
384+
const root1 = '0x0000000000000000000000000000000000000000000000000000000000000001'
385+
const root2 = '0x0000000000000000000000000000000000000000000000000000000000000002'
386+
const root3 = '0x0000000000000000000000000000000000000000000000000000000000000003'
387+
388+
async function setParentBlockRoot(slot, root) {
389+
await beaconRootsMock.setParentBlockRoot(beaconGenesisTime + (slot * secondsPerSlot), root);
390+
}
391+
392+
await setParentBlockRoot(501n, root1);
393+
await setParentBlockRoot(502n, root2);
394+
// Simulate 2 skipped slots
395+
await setParentBlockRoot(505n, root3);
396+
397+
assert.equal(await blockRoots.getBlockRoot(500n), root1);
398+
assert.equal(await blockRoots.getBlockRoot(501n), root2);
399+
assert.equal(await blockRoots.getBlockRoot(502n), root3);
400+
assert.equal(await blockRoots.getBlockRoot(503n), root3);
401+
assert.equal(await blockRoots.getBlockRoot(504n), root3);
402+
await shouldRevert(
403+
blockRoots.getBlockRoot(505n),
404+
"Did not revert on invalid slot",
405+
"Block root is not available"
406+
);
407+
});
408+
409+
it(printTitle('BlockRoots', 'Fails to return a block root for a slot that is too old'), async () => {
410+
const block = await ethers.provider.getBlock();
411+
const beaconGenesisTime = (BigInt(block.timestamp) / 12n * 12n);
412+
const secondsPerSlot = 12n
413+
414+
const BeaconRootsMock = artifacts.require('BeaconRootsMock');
415+
const BlockRoots = artifacts.require('BlockRoots');
416+
const beaconRootsMock = await BeaconRootsMock.new();
417+
const blockRoots = await BlockRoots.new(beaconGenesisTime, secondsPerSlot, 10n, beaconRootsMock.target);
418+
419+
await time.increaseTo(beaconGenesisTime + (secondsPerSlot * 100n));
420+
421+
/**
422+
* We've set the genesis time such that we are now at slot 100
423+
* And we've set the history buffer length to 10
424+
*
425+
* That means the EVM theoretically has parent block hashes for slots 91 to 100 or in other words, the
426+
* block hashes for blocks in slots 90 to 99
427+
*
428+
* Therefore, retrieving the block hash for 89 should revert with "Slot too old" but not 90
429+
*/
430+
431+
await shouldRevert(
432+
blockRoots.getBlockRoot(89n),
433+
"Was able to get old block root",
434+
"Slot too old"
435+
);
436+
437+
// We didn't actually mock the parent block hash so just check that we don't revert with "Slot too old"
438+
await shouldRevert(
439+
blockRoots.getBlockRoot(90n),
440+
"Incorrectly reverted with slot too old",
441+
"Block root is not available"
442+
);
443+
});
371444
});
372445
}

0 commit comments

Comments
 (0)