Skip to content

Commit

Permalink
wip: challenge proof components poc
Browse files Browse the repository at this point in the history
  • Loading branch information
merklefruit committed Sep 11, 2024
1 parent 9794de8 commit 9590b5a
Show file tree
Hide file tree
Showing 13 changed files with 2,819 additions and 0 deletions.
3 changes: 3 additions & 0 deletions bolt-contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ out/
broadcast/

.env

node_modules/
target/
87 changes: 87 additions & 0 deletions bolt-contracts/src/contracts/BoltChallenger.sol
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();
}
}
189 changes: 189 additions & 0 deletions bolt-contracts/src/lib/BytesUtils.sol
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);
}
}
Loading

0 comments on commit 9590b5a

Please sign in to comment.