-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9794de8
commit 9590b5a
Showing
13 changed files
with
2,819 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ out/ | |
broadcast/ | ||
|
||
.env | ||
|
||
node_modules/ | ||
target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {SecureMerkleTrie} from "../lib/trie/SecureMerkleTrie.sol"; | ||
import {RLPReader} from "../lib/rlp/RLPReader.sol"; | ||
|
||
contract BoltChallenger { | ||
using RLPReader for bytes; | ||
using RLPReader for RLPReader.RLPItem; | ||
|
||
error BlockIsTooOld(); | ||
error InvalidBlockHash(); | ||
error AccountDoesNotExist(); | ||
|
||
constructor() {} | ||
|
||
function openChallenge() public { | ||
// unimplemented!(); | ||
} | ||
|
||
function resolveChallenge( | ||
uint256 challengeId | ||
) public { | ||
// unimplemented!(); | ||
} | ||
|
||
/// @dev Only works with block headers that are less than 256 blocks old. | ||
function proveRecentBlockHeaderData( | ||
bytes calldata header | ||
) | ||
public | ||
view | ||
returns ( | ||
bytes32 transactionsRoot, | ||
uint256 blockNumber, | ||
uint256 gasLimit, | ||
uint256 gasUsed, | ||
uint256 timestamp, | ||
uint256 baseFee | ||
) | ||
{ | ||
// RLP decode the header | ||
// https://github.com/ethereum/go-ethereum/blob/master/core/types/block.go | ||
RLPReader.RLPItem[] memory headerFields = header.toRLPItem().readList(); | ||
transactionsRoot = headerFields[4].readBytes32(); | ||
blockNumber = headerFields[8].readUint256(); | ||
gasLimit = headerFields[9].readUint256(); | ||
gasUsed = headerFields[10].readUint256(); | ||
timestamp = headerFields[11].readUint256(); | ||
baseFee = headerFields[15].readUint256(); | ||
|
||
if (blockhash(blockNumber) == bytes32(0) || blockNumber < block.number - 256) { | ||
revert BlockIsTooOld(); | ||
} | ||
|
||
// verify that the block hash matches the one in the EVM | ||
if (keccak256(header) != blockhash(blockNumber)) { | ||
revert InvalidBlockHash(); | ||
} | ||
} | ||
|
||
/// @notice Prove the account data of an account at a given state root. | ||
/// @dev This function assumes that the provided state root and account proof match. | ||
/// @param account The account address to prove. | ||
/// @param stateRoot The TRUSTED state root to prove against. Checking how the state root is obtained | ||
/// is the responsibility of the caller. | ||
/// @param accountProof The MPT account proof to prove the account data. | ||
/// @return nonce The nonce of the account at the given state root height. | ||
/// @return balance The balance of the account at the given state root height. | ||
function proveAccountData( | ||
address account, | ||
bytes32 stateRoot, | ||
bytes calldata accountProof | ||
) public returns (uint256 nonce, uint256 balance) { | ||
(bool exists, bytes memory accountRLP) = | ||
SecureMerkleTrie.get(abi.encodePacked(account), accountProof, stateRoot); | ||
|
||
if (!exists) { | ||
revert AccountDoesNotExist(); | ||
} | ||
|
||
// decode the account RLP into nonce and balance | ||
RLPReader.RLPItem[] memory accountFields = accountRLP.toRLPItem().readList(); | ||
nonce = accountFields[0].readUint256(); | ||
balance = accountFields[1].readUint256(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
/** | ||
* @title BytesUtils | ||
*/ | ||
library BytesUtils { | ||
/** | ||
* | ||
* Internal Functions * | ||
* | ||
*/ | ||
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { | ||
unchecked { | ||
require(_length + 31 >= _length, "slice_overflow"); | ||
require(_start + _length >= _start, "slice_overflow"); | ||
require(_bytes.length >= _start + _length, "slice_outOfBounds"); | ||
|
||
bytes memory tempBytes; | ||
|
||
assembly { | ||
switch iszero(_length) | ||
case 0 { | ||
// Get a location of some free memory and store it in tempBytes as | ||
// Solidity does for memory variables. | ||
tempBytes := mload(0x40) | ||
|
||
// The first word of the slice result is potentially a partial | ||
// word read from the original array. To read it, we calculate | ||
// the length of that partial word and start copying that many | ||
// bytes into the array. The first word we copy will start with | ||
// data we don't care about, but the last `lengthmod` bytes will | ||
// land at the beginning of the contents of the new array. When | ||
// we're done copying, we overwrite the full first word with | ||
// the actual length of the slice. | ||
let lengthmod := and(_length, 31) | ||
|
||
// The multiplication in the next line is necessary | ||
// because when slicing multiples of 32 bytes (lengthmod == 0) | ||
// the following copy loop was copying the origin's length | ||
// and then ending prematurely not copying everything it should. | ||
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) | ||
let end := add(mc, _length) | ||
|
||
for { | ||
// The multiplication in the next line has the same exact purpose | ||
// as the one above. | ||
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) | ||
} lt(mc, end) { | ||
mc := add(mc, 0x20) | ||
cc := add(cc, 0x20) | ||
} { mstore(mc, mload(cc)) } | ||
|
||
mstore(tempBytes, _length) | ||
|
||
//update free-memory pointer | ||
//allocating the array padded to 32 bytes like the compiler does now | ||
mstore(0x40, and(add(mc, 31), not(31))) | ||
} | ||
//if we want a zero-length slice let's just return a zero-length array | ||
default { | ||
tempBytes := mload(0x40) | ||
|
||
//zero out the 32 bytes slice we are about to return | ||
//we need to do it because Solidity does not garbage collect | ||
mstore(tempBytes, 0) | ||
|
||
mstore(0x40, add(tempBytes, 0x20)) | ||
} | ||
} | ||
|
||
return tempBytes; | ||
} | ||
} | ||
|
||
function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) { | ||
unchecked { | ||
if (_bytes.length - _start == 0) { | ||
return bytes(""); | ||
} | ||
|
||
return slice(_bytes, _start, _bytes.length - _start); | ||
} | ||
} | ||
|
||
function toBytes32PadLeft( | ||
bytes memory _bytes | ||
) internal pure returns (bytes32) { | ||
unchecked { | ||
bytes32 ret; | ||
uint256 len = _bytes.length <= 32 ? _bytes.length : 32; | ||
assembly { | ||
ret := shr(mul(sub(32, len), 8), mload(add(_bytes, 32))) | ||
} | ||
return ret; | ||
} | ||
} | ||
|
||
function toBytes32( | ||
bytes memory _bytes | ||
) internal pure returns (bytes32) { | ||
unchecked { | ||
if (_bytes.length < 32) { | ||
bytes32 ret; | ||
assembly { | ||
ret := mload(add(_bytes, 32)) | ||
} | ||
return ret; | ||
} | ||
|
||
return abi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes | ||
} | ||
} | ||
|
||
function toUint256( | ||
bytes memory _bytes | ||
) internal pure returns (uint256) { | ||
return uint256(toBytes32(_bytes)); | ||
} | ||
|
||
function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { | ||
require(_start + 3 >= _start, "toUint24_overflow"); | ||
require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); | ||
uint24 tempUint; | ||
|
||
assembly { | ||
tempUint := mload(add(add(_bytes, 0x3), _start)) | ||
} | ||
|
||
return tempUint; | ||
} | ||
|
||
function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { | ||
require(_start + 1 >= _start, "toUint8_overflow"); | ||
require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); | ||
uint8 tempUint; | ||
|
||
assembly { | ||
tempUint := mload(add(add(_bytes, 0x1), _start)) | ||
} | ||
|
||
return tempUint; | ||
} | ||
|
||
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { | ||
require(_start + 20 >= _start, "toAddress_overflow"); | ||
require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); | ||
address tempAddress; | ||
|
||
assembly { | ||
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) | ||
} | ||
|
||
return tempAddress; | ||
} | ||
|
||
function toNibbles( | ||
bytes memory _bytes | ||
) internal pure returns (bytes memory) { | ||
unchecked { | ||
bytes memory nibbles = new bytes(_bytes.length * 2); | ||
|
||
for (uint256 i = 0; i < _bytes.length; i++) { | ||
nibbles[i * 2] = _bytes[i] >> 4; | ||
nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); | ||
} | ||
|
||
return nibbles; | ||
} | ||
} | ||
|
||
function fromNibbles( | ||
bytes memory _bytes | ||
) internal pure returns (bytes memory) { | ||
unchecked { | ||
bytes memory ret = new bytes(_bytes.length / 2); | ||
|
||
for (uint256 i = 0; i < ret.length; i++) { | ||
ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); | ||
} | ||
|
||
return ret; | ||
} | ||
} | ||
|
||
function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { | ||
return keccak256(_bytes) == keccak256(_other); | ||
} | ||
} |
Oops, something went wrong.