-
Notifications
You must be signed in to change notification settings - Fork 0
/
BlockhashStore.sol
73 lines (62 loc) · 2.69 KB
/
BlockhashStore.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
pragma solidity 0.6.6;
/**
* @title BlockhashStore
* @notice This contract provides a way to access blockhashes older than
* the 256 block limit imposed by the BLOCKHASH opcode.
* You may assume that any blockhash stored by the contract is correct.
*/
contract BlockhashStore {
mapping(uint => bytes32) internal s_blockhashes;
/**
* @notice stores blockhash of a given block, assuming it is available through BLOCKHASH
* @param n the number of the block whose blockhash should be stored
*/
function store(uint256 n) public {
bytes32 h = blockhash(n);
require(h != 0x0, "blockhash(n) failed");
s_blockhashes[n] = h;
}
/**
* @notice stores blockhash of the earliest block still available through BLOCKHASH.
*/
function storeEarliest() external {
store(block.number - 256);
}
/**
* @notice stores blockhash after verifying blockheader of child/subsequent block
* @param n the number of the block whose blockhash should be stored
* @param header the rlp-encoded blockheader of block n+1. We verify its correctness by checking
* that it hashes to a stored blockhash, and then extract parentHash to get the n-th blockhash.
*/
function storeVerifyHeader(uint256 n, bytes memory header) public {
require(keccak256(header) == s_blockhashes[n + 1], "header has unknown blockhash");
// At this point, we know that header is the correct blockheader for block n+1.
// The header is an rlp-encoded list. The head item of that list is the 32-byte blockhash of the parent block.
// Based on how rlp works, we know that blockheaders always have the following form:
// 0xf9____a0PARENTHASH...
// ^ ^ ^
// | | |
// | | +--- PARENTHASH is 32 bytes. rlpenc(PARENTHASH) is 0xa || PARENTHASH.
// | |
// | +--- 2 bytes containing the sum of the lengths of the encoded list items
// |
// +--- 0xf9 because we have a list and (sum of lengths of encoded list items) fits exactly into two bytes.
//
// As a consequence, the PARENTHASH is always at offset 4 of the rlp-encoded block header.
bytes32 parentHash;
assembly {
parentHash := mload(add(header, 36)) // 36 = 32 byte offset for length prefix of ABI-encoded array
// + 4 byte offset of PARENTHASH (see above)
}
s_blockhashes[n] = parentHash;
}
/**
* @notice gets a blockhash from the store. If no hash is known, this function reverts.
* @param n the number of the block whose blockhash should be returned
*/
function getBlockhash(uint256 n) external view returns (bytes32) {
bytes32 h = s_blockhashes[n];
require(h != 0x0, "blockhash not found in store");
return h;
}
}