Commit 069a7e1e authored by George Hotz's avatar George Hotz Committed by GitHub

Merge branch 'master' into fix/delete-node-sister-not-resolved

parents a7688596 57f989f2
node_modules node_modules
\ No newline at end of file artifacts
cache
Two oracles: One oracle:
Implement as three MIPS instructions Implement as a MIPS instruction?
InputOracle -- Preagreed upon inputs InputOracle -- Preagreed upon inputs (TODO: replace with memory reads from given address range)
$a0 = input choice $a0 = input choice
0 -- StateRoot(n) 0 -- BlockHash(n)
1 -- Transactions(n+1) 1 -- Transactions(n+1)
2 -- Coinbase(n+1) 2 -- Coinbase(n+1)
3 -- Uncles(n+1) 3 -- Uncles(n+1)
...@@ -24,31 +24,36 @@ PreimageOracle -- key value store ...@@ -24,31 +24,36 @@ PreimageOracle -- key value store
returns returns
$v0 = preimage[$t7...$t0] >> ($a0 * 32) $v0 = preimage[$t7...$t0] >> ($a0 * 32)
Program returns a hash in [$t7...$t0] and exits(special instruction) with the hash in the state Program returns a hash in [$t7...$t0] and exits(jump to 0xDEAD0000) with the hash in the state
Challenge Flow: Challenge Flow:
C is challenger, D is defender C is challenger, D is defender
Super nice, the defender barely needs to spend gas! Super nice, the defender barely needs to spend gas!
C: InitiateChallenge(bytes blockHeaderN, bytes blockHeaderNp1, C: InitiateChallenge(uint blockNumberN, bytes blockHeaderN, bytes blockHeaderNp1,
bytes32 assertionHash, bytes32 finalSystemHash, string[] assertionProof, uint256 stepCount) bytes32 assertionHash, bytes32 finalSystemHash, string[] assertionProof, uint256 stepCount)
* checks hashes of the block headers * checks hashes of the block headers
* saves inputs for input oracle * saves inputs for input oracle
* confirms assertionHash != blockHeaderNp1.Hash * confirms assertionHash != blockHeaderNp1.Hash
* expectCorrect = (assertionHash == blockHeaderNp1.Hash) * confirm assertionProof[0..7] proves the final state of [$t7...$t0] in finalSystemHash is assertionHash
* confirm assertionProof proves the final state of [$t7...$t0] in finalSystemHash is assertionHash * confirm assertionProof[8] proves the final state of $pc in finalSystemHash is 0xDEAD0000
* confirm assertionProof proves the final state of *$pc in finalSystemHash is special exit instruction * L = 0, R = stepCount # we agree at L=0, we disagree at R=stepCount
* L = 0, R = stepCount-1 # we agree at L=0, we disagree at R=stepCount
* return new challengeId * return new challengeId
* assertedRiscState[0] = GlobalStartSystemHash * assertedState[0] = GlobalStartSystemHash + inputOracleMutations
* assertedRiscState[stepCount-1] = finalSystemHash * defendedState[0] = GlobalStartSystemHash + inputOracleMutations
C: ProposeRiscState(uint256 challengeId, uint256 riscState) * assertedState[stepCount] = finalSystemHash
........
if it's one step, we are done. considering it's not, we binary search
........
C: ProposeState(uint256 challengeId, uint256 riscState)
* stepNumber = GetStepNumber(uint256 challengeId) returns floor((L + R) / 2) * stepNumber = GetStepNumber(uint256 challengeId) returns floor((L + R) / 2)
* assert assertedRiscState[stepNumber] == 0 * assert assertedState[stepNumber] == 0
* assertedRiscState[stepNumber] = riscState * assertedState[stepNumber] = riscState
D: RespondRiscState(uint256 challengeId, bool yes) onlyOwner D: RespondState(uint256 challengeId, uint256 riscState) onlyOwner
* off-chain: run to step = stepNumber, get state hash, check if it matches * off-chain: run to step = stepNumber, get state hash, check if it matches
* if yes: * stepNumber = GetStepNumber(uint256 challengeId) returns floor((L + R) / 2)
* defendedState[stepNumber] = riscState
* if assertedState[stepNumber] == defendedState[stepNumber]:
L = stepNumber # we agree at stepNumber L = stepNumber # we agree at stepNumber
else: else:
R = stepNumber # we disagree at stepNumber R = stepNumber # we disagree at stepNumber
...@@ -56,16 +61,27 @@ D: RespondRiscState(uint256 challengeId, bool yes) onlyOwner ...@@ -56,16 +61,27 @@ D: RespondRiscState(uint256 challengeId, bool yes) onlyOwner
........ ........
binary search until L+1 == R binary search until L+1 == R
the issue is with the L->R transition the issue is with the L->R transition
aka assertedRiscState[L] -> assertedRiscState[R] aka assertedState[L] -> assertedState[R]
........ ........
# call this at any time (global), adds them to a preimage lookup for PreimageOracle # call this at any time (global), adds them to a preimage lookup for PreimageOracle
C: ProposePreimage(bytes anything) # put these on the MIPS contract
C: AddPreimage(bytes anything)
* preimageLookup[keccak256(anything)] = anything * preimageLookup[keccak256(anything)] = anything
C: ConfirmStateTransition(uint256 challengeId, ustrings proofs) C: AddMerkleState(uint256 stateHash, uint32 addr, uint32 value, string proof)
* validate proof in assertedState[stepNumber]
* riscMemory[stepNumber][address] = value
* Final
C: ConfirmStateTransition(uint256 challengeId)
* assert L+1 == R * assert L+1 == R
* validate all proofs in assertedRiscState[L]
* do the state transition * do the state transition
* if any needed pieces of start state are missing, challenge fails (it can try again) * if any needed pieces of start state are missing in riscMemory, challenge fails (it can try again)
* reconstruct the riscState after transition -> newRiscState * reconstruct the riscState after transition -> newState
* assert assertedRiscState[R] == newRiscState * assert assertedState[R] == newState
* pay out bounty * pay out bounty
# optional claim for the defender
# prove the defendedState[L] -> defendedState[R]
# NOTE, if it's the last step, defendedState[R] might not exist.
TODO: ensure the state merklization is canonical. if it doesn't match perfectly you can lose
\ No newline at end of file
...@@ -21,6 +21,14 @@ The system is checking an embedded block in CI now ...@@ -21,6 +21,14 @@ The system is checking an embedded block in CI now
* TODO: Get initial block info / txs with oracle * TODO: Get initial block info / txs with oracle
* TODO: Stub all syscalls after it's "booted" * TODO: Stub all syscalls after it's "booted"
## TODO
* Make minigeth use the 0xD0000000 input oracle / add input oracle to risc/run.py. See [contracts/Challenge.sol](https://github.com/geohot/cannon/blob/master/contracts/Challenge.sol)
* Add blocks / tx trie to the preimage oracle [see issue](https://github.com/geohot/cannon/issues/2)
* Finish and test the MIPS state transition function in contracts/MIPS.sol
* Get minigeth running in Solidity MIPS emulator with reasonable performance (Go code using EVM with native memory)
* Add merkleization for MIPS ReadMemory and WriteMemory
## Usage ## Usage
``` ```
# verify the transition from 13284469 -> 13284470 # verify the transition from 13284469 -> 13284470
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.3;
import "./lib/Lib_RLPReader.sol";
interface IMIPS {
function Step(bytes32 stateHash) external view returns (bytes32);
function ReadMemory(bytes32 stateHash, uint32 addr) external view returns (uint32);
function ReadBytes32(bytes32 stateHash, uint32 addr) external view returns (bytes32);
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32);
}
contract Challenge {
address payable immutable owner;
// the mips machine state transition function
IMIPS immutable mips;
// the program start state
bytes32 immutable GlobalStartState;
struct Chal {
uint256 L;
uint256 R;
mapping(uint256 => bytes32) assertedState;
mapping(uint256 => bytes32) defendedState;
address payable challenger;
}
mapping(uint256 => Chal) challenges;
constructor(IMIPS imips, bytes32 globalStartState) {
owner = msg.sender;
mips = imips;
GlobalStartState = globalStartState;
}
// allow getting money (and withdrawing the bounty, honor system)
fallback() external payable {}
receive() external payable {}
function withdraw() external {
require(msg.sender == owner);
owner.transfer(address(this).balance);
}
// memory helpers
function writeBytes32(bytes32 stateHash, uint32 addr, bytes32 val) internal view returns (bytes32) {
for (uint32 i = 0; i < 32; i += 4) {
uint256 tv = uint256(val>>(224-(i*8)));
stateHash = mips.WriteMemory(stateHash, addr+i, uint32(tv));
}
return stateHash;
}
// create challenge
uint256 public lastChallengeId = 0;
function newChallengeTrusted(bytes32 startState, bytes32 finalSystemState, uint256 stepCount) internal returns (uint256) {
uint256 challengeId = lastChallengeId;
Chal storage c = challenges[challengeId];
lastChallengeId += 1;
// the challenger arrives
c.challenger = msg.sender;
// the state is set
c.assertedState[0] = startState;
c.defendedState[0] = startState;
c.assertedState[stepCount] = finalSystemState;
// init the binary search
c.L = 0;
c.R = stepCount;
// find me later
return challengeId;
}
function InitiateChallenge(uint blockNumberN, bytes calldata blockHeaderNp1,
bytes32 assertionRoot, bytes32 finalSystemState, uint256 stepCount) external returns (uint256) {
require(blockhash(blockNumberN+1) == keccak256(blockHeaderNp1), "end block hash wrong");
// decode the blocks
Lib_RLPReader.RLPItem[] memory blockNp1 = Lib_RLPReader.readList(blockHeaderNp1);
bytes32 parentHash = Lib_RLPReader.readBytes32(blockNp1[0]);
require(blockhash(blockNumberN) == parentHash, "parent block hash somehow wrong");
bytes32 newroot = Lib_RLPReader.readBytes32(blockNp1[3]);
require(assertionRoot != newroot, "asserting that the real state is correct is not a challenge");
// load starting info into the input oracle
// we both agree at the beginning
// the first instruction executed in MIPS should be an access of startState
// parentblockhash, txhash, coinbase, unclehash, gaslimit
bytes32 startState = GlobalStartState;
startState = writeBytes32(startState, 0xD0000000, parentHash);
startState = writeBytes32(startState, 0xD0000020, Lib_RLPReader.readBytes32(blockNp1[4]));
startState = writeBytes32(startState, 0xD0000040, bytes32(uint256(Lib_RLPReader.readAddress(blockNp1[2]))));
startState = writeBytes32(startState, 0xD0000060, Lib_RLPReader.readBytes32(blockNp1[1]));
startState = writeBytes32(startState, 0xD0000080, bytes32(Lib_RLPReader.readUint256(blockNp1[9])));
// confirm the finalSystemHash asserts the state you claim (in $t0-$t7) and the machine is stopped
// you must load these proofs into MIPS before calling this
// we disagree at the end
require(mips.ReadBytes32(finalSystemState, 0xC0000020) == assertionRoot, "you are claiming a different state root in machine");
require(mips.ReadMemory(finalSystemState, 0xC0000080) == 0xDEAD0000, "machine is not stopped in final state (PC == 0xDEAD0000)");
return newChallengeTrusted(startState, finalSystemState, stepCount);
}
// binary search
function getStepNumber(uint256 challengeId) view public returns (uint256) {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
return (c.L+c.R)/2;
}
function ProposeState(uint256 challengeId, bytes32 riscState) external {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(c.challenger == msg.sender, "must be challenger");
uint256 stepNumber = getStepNumber(challengeId);
require(c.assertedState[stepNumber] == bytes32(0), "state already proposed");
c.assertedState[stepNumber] = riscState;
}
function RespondState(uint256 challengeId, bytes32 riscState) external {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(owner == msg.sender, "must be owner");
uint256 stepNumber = getStepNumber(challengeId);
require(c.assertedState[stepNumber] != bytes32(0), "challenger state not proposed");
require(c.defendedState[stepNumber] == bytes32(0), "state already proposed");
// technically, we don't have to save these states
// but if we want to prove us right and not just the attacker wrong, we do
c.defendedState[stepNumber] = riscState;
if (c.assertedState[stepNumber] == c.defendedState[stepNumber]) {
// agree
c.L = stepNumber;
} else {
// disagree
c.R = stepNumber;
}
}
// final payout
event ChallengerWins(uint256 challengeId);
event ChallengerLoses(uint256 challengeId);
function ConfirmStateTransition(uint256 challengeId) external {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(c.challenger == msg.sender, "must be challenger");
require(c.L + 1 == c.R, "binary search not finished");
require(mips.Step(c.assertedState[c.L]) == c.assertedState[c.R], "wrong asserted state");
// pay out bounty!!
c.challenger.transfer(address(this).balance);
emit ChallengerWins(challengeId);
}
function MockChallengerStateTransition(uint256 challengeId, bytes32 finalRiscState) external {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(owner == msg.sender, "must be owner");
require(c.L + 1 == c.R, "binary search not finished");
// it's 0 if you agree with all attacker states except the final one
// in which case, you get a free pass to submit now
require(c.defendedState[c.R] == finalRiscState || c.defendedState[c.R] == bytes32(0), "must be consistent with state");
require(mips.Step(c.defendedState[c.L]) == finalRiscState, "wrong asserted state");
// consider the challenger mocked
// if they staked a bounty, you could claim it here
emit ChallengerLoses(challengeId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.3;
// https://inst.eecs.berkeley.edu/~cs61c/resources/MIPS_Green_Sheet.pdf
// https://uweb.engr.arizona.edu/~ece369/Resources/spim/MIPSReference.pdf
// This is a separate contract from the challenge contract
// Anyone can use it to validate a MIPS state transition
// First, to prepare, you call AddMerkleState, which adds valid state nodes in the stateHash.
// If you are using the Preimage oracle, you call AddPreimage
// Then, you call Step. Step will revert if state is missing. If all state is present, it will return the next hash
contract MIPS {
// This state is global
mapping(bytes32 => mapping (uint32 => uint64)) public state;
mapping(bytes32 => bytes) public preimage;
function AddPreimage(bytes calldata anything) public {
preimage[keccak256(anything)] = anything;
}
function AddMerkleState(bytes32 stateHash, uint32 addr, uint32 value, string calldata proof) public {
// TODO: check proof
state[stateHash][addr] = (1 << 32) | value;
}
uint32 constant public REG_OFFSET = 0xc0000000;
uint32 constant public REG_PC = REG_OFFSET + 0x20*4;
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) public pure returns (bytes32) {
// TODO: does the stateHash mutation
require(addr & 3 == 0, "write memory must be 32-bit aligned");
}
// needed for preimage oracle
function ReadBytes32(bytes32 stateHash, uint32 addr) public view returns (bytes32) {
uint256 ret = 0;
for (uint32 i = 0; i < 32; i += 4) {
ret <<= 32;
ret |= uint256(ReadMemory(stateHash, addr+i));
}
return bytes32(ret);
}
function ReadMemory(bytes32 stateHash, uint32 addr) public view returns (uint32) {
if (addr == REG_OFFSET) {
// zero register is always 0
return 0;
}
require(addr & 3 == 0, "read memory must be 32-bit aligned");
uint64 ret = state[stateHash][addr];
require((ret >> 32) == 1, "memory was not initialized");
return uint32(ret);
}
// compute the next state
// will revert if any required input state is missing
function Step(bytes32 stateHash) public view returns (bytes32) {
// instruction fetch
uint32 pc = ReadMemory(stateHash, REG_PC);
uint32 insn = ReadMemory(stateHash, pc);
uint32 opcode = insn >> 26; // 6-bits
// decode
// register fetch
uint32 rs;
uint32 rt;
if (opcode != 2 && opcode != 3) { // J-type: j and jal have no register fetch
// R-type or I-type (stores rt)
rs = ReadMemory(stateHash, REG_OFFSET + ((insn >> 19) & 0x7C));
if (opcode == 0) {
// R-type (stores rd)
rt = ReadMemory(stateHash, REG_OFFSET + ((insn >> 14) & 0x7C));
}
}
// memory fetch (all I-type)
// we do the load for stores also
uint32 mem;
if (opcode >= 0x20) {
// M[R[rs]+SignExtImm]
uint32 SignExtImm = insn&0xFFFF | (insn&0x8000 != 0 ? 0xFFFF0000 : 0);
mem = ReadMemory(stateHash, (rs + SignExtImm) & 0xFFFFFFFC);
}
// execute
execute(insn, rs, rt, mem);
// write back
}
// TODO: move pure testable stuff to LibMIPS.sol
function execute(uint32 insn, uint32 rs, uint32 rt, uint32 mem) public pure returns (uint32) {
uint32 opcode = insn >> 26; // 6-bits
uint32 func = insn & 0x3f; // 6-bits
// TODO: deref the immed into a register
if (opcode == 0) {
uint32 shamt = (insn >> 6) & 0x1f;
// R-type (ArithLog)
if (func == 0x20 || func == 0x21) { return rs+rt; // add or addu
} else if (func == 0x24) { return rs&rt; // and
} else if (func == 0x27) { return ~(rs|rt); // nor
} else if (func == 0x25) { return (rs|rt); // or
} else if (func == 0x22 || func == 0x23) {
return rs-rt; // sub or subu
} else if (func == 0x2a) {
return int32(rs)<int32(rt) ? 1 : 0; // slt
} else if (func == 0x26) {
return rs<rt ? 1 : 0; // sltu
// Shift and ShiftV
} else if (func == 0x00) { return rt << shamt; // sll
} else if (func == 0x04) { return rt << rs; // sllv
} else if (func == 0x03) { return rt >> shamt; // sra
} else if (func == 0x07) { return rt >> rs; // srav
} else if (func == 0x02) { return rt >> shamt; // srl
} else if (func == 0x06) { return rt >> rs; // srlv
}
} else if (func == 0x20) { return mem; // lb
} else if (func == 0x24) { return mem; // lbu
} else if (func == 0x21) { return mem; // lh
} else if (func == 0x25) { return mem; // lhu
} else if (func == 0x23) { return mem; // lw
} else if (func&0x3c == 0x28) { return rt; // sb, sh, sw
}
}
}
This diff is collapsed.
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.7.3",
};
...@@ -101,7 +101,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. ...@@ -101,7 +101,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles) accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
//fmt.Println("new Root", header.Root) fmt.Println("new Root", header.Root)
} }
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
......
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package misc
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559,
// - gas limit check
// - basefee check
func VerifyEip1559Header(config *params.ChainConfig, parent, header *types.Header) error {
// Verify that the gas limit remains within allowed bounds
parentGasLimit := parent.GasLimit
if !config.IsLondon(parent.Number) {
parentGasLimit = parent.GasLimit * params.ElasticityMultiplier
}
if err := VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil {
return err
}
// Verify the header is not malformed
if header.BaseFee == nil {
return fmt.Errorf("header is missing baseFee")
}
// Verify the baseFee is correct based on the parent header.
expectedBaseFee := CalcBaseFee(config, parent)
if header.BaseFee.Cmp(expectedBaseFee) != 0 {
return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d",
expectedBaseFee, header.BaseFee, parent.BaseFee, parent.GasUsed)
}
return nil
}
// CalcBaseFee calculates the basefee of the header.
func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsLondon(parent.Number) {
return new(big.Int).SetUint64(params.InitialBaseFee)
}
var (
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator)
)
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return new(big.Int).Set(parent.BaseFee)
}
if parent.GasUsed > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
gasUsedDelta := new(big.Int).SetUint64(parent.GasUsed - parentGasTarget)
x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
y := x.Div(x, parentGasTargetBig)
baseFeeDelta := math.BigMax(
x.Div(y, baseFeeChangeDenominator),
common.Big1,
)
return x.Add(parent.BaseFee, baseFeeDelta)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - parent.GasUsed)
x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
y := x.Div(x, parentGasTargetBig)
baseFeeDelta := x.Div(y, baseFeeChangeDenominator)
return math.BigMax(
x.Sub(parent.BaseFee, baseFeeDelta),
common.Big0,
)
}
}
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package misc
import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/params"
)
// VerifyGaslimit verifies the header gas limit according increase/decrease
// in relation to the parent gas limit.
func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error {
// Verify that the gas limit remains within allowed bounds
diff := int64(parentGasLimit) - int64(headerGasLimit)
if diff < 0 {
diff *= -1
}
limit := parentGasLimit / params.GasLimitBoundDivisor
if uint64(diff) >= limit {
return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1)
}
if headerGasLimit < params.MinGasLimit {
return errors.New("invalid gas limit below 5000")
}
return nil
}
...@@ -2,14 +2,19 @@ package main ...@@ -2,14 +2,19 @@ package main
import ( import (
"fmt" "fmt"
"log"
"math/big"
"os" "os"
"strconv" "strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/oracle"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
...@@ -24,29 +29,33 @@ func main() { ...@@ -24,29 +29,33 @@ func main() {
blockNumber, _ := strconv.Atoi(os.Args[1]) blockNumber, _ := strconv.Atoi(os.Args[1])
// read header // non mips
var header types.Header oracle.PrefetchBlock(big.NewInt(int64(blockNumber)), true, trie.NewStackTrie(nil))
{ oracle.PrefetchBlock(big.NewInt(int64(blockNumber)+1), false, trie.NewStackTrie(nil))
f, _ := os.Open(fmt.Sprintf("data/block_%d", blockNumber))
rlpheader := rlp.NewStream(f, 0) // read start block header
rlpheader.Decode(&header) var parent types.Header
f.Close() rlperr := rlp.DecodeBytes(oracle.Preimage(oracle.Input(0)), &parent)
fmt.Println("read old block") if rlperr != nil {
log.Fatal(rlperr)
} }
// read header // read header
var newheader types.Header var newheader types.Header
{ // from parent
f, _ := os.Open(fmt.Sprintf("data/block_%d", blockNumber+1)) newheader.ParentHash = parent.Hash()
rlpheader := rlp.NewStream(f, 0) newheader.Number = big.NewInt(0).Add(parent.Number, big.NewInt(1))
rlpheader.Decode(&newheader) newheader.BaseFee = misc.CalcBaseFee(params.MainnetChainConfig, &parent)
f.Close()
fmt.Println("read new block") // from input oracle
} newheader.TxHash = oracle.Input(1)
newheader.Coinbase = common.BigToAddress(oracle.Input(2).Big())
newheader.UncleHash = oracle.Input(3)
newheader.GasLimit = oracle.Input(4).Big().Uint64()
bc := core.NewBlockChain() bc := core.NewBlockChain()
database := state.NewDatabase(header) database := state.NewDatabase(parent)
statedb, _ := state.New(header.Root, database, nil) statedb, _ := state.New(parent.Root, database, nil)
vmconfig := vm.Config{} vmconfig := vm.Config{}
processor := core.NewStateProcessor(params.MainnetChainConfig, bc, bc.Engine()) processor := core.NewStateProcessor(params.MainnetChainConfig, bc, bc.Engine())
fmt.Println("made state processor") fmt.Println("made state processor")
...@@ -64,23 +73,23 @@ func main() { ...@@ -64,23 +73,23 @@ func main() {
var uncles []*types.Header var uncles []*types.Header
var receipts []*types.Receipt var receipts []*types.Receipt
block := types.NewBlock(&newheader, txs, uncles, receipts, trie.NewStackTrie(nil)) block := types.NewBlock(&newheader, txs, uncles, receipts, trie.NewStackTrie(nil))
fmt.Println("made block, parent:", header.ParentHash) fmt.Println("made block, parent:", newheader.ParentHash)
// if this is correct, the trie is working // if this is correct, the trie is working
// TODO: it's the previous block now // TODO: it's the previous block now
if newheader.TxHash != block.Header().TxHash { if newheader.TxHash != block.Header().TxHash {
panic("wrong transactions for block") panic("wrong transactions for block")
} }
if newheader.UncleHash != block.Header().UncleHash {
panic("wrong uncles for block")
}
_, _, _, err := processor.Process(block, statedb, vmconfig) _, _, _, err := processor.Process(block, statedb, vmconfig)
if err != nil { if err != nil {
panic("processor error") log.Fatal(err)
} }
newroot := statedb.IntermediateRoot(bc.Config().IsEIP158(newheader.Number))
fmt.Println("process done with hash", header.Root, "->", block.Header().Root, "real", newheader.Root) fmt.Println("process done with hash", parent.Root, "->", newroot)
if block.Header().Root == newheader.Root { oracle.Output(newroot)
fmt.Println("good transition")
} else {
panic("BAD transition :((")
}
} }
//go:build !mips
// +build !mips
package oracle
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
// SendTxArgs represents the arguments to submit a transaction
// This struct is identical to ethapi.TransactionArgs, except for the usage of
// common.MixedcaseAddress in From and To
type SendTxArgs struct {
From common.MixedcaseAddress `json:"from"`
To *common.MixedcaseAddress `json:"to"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
Value hexutil.Big `json:"value"`
Nonce hexutil.Uint64 `json:"nonce"`
// We accept "data" and "input" for backwards-compatibility reasons.
// "input" is the newer name and should be preferred by clients.
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input,omitempty"`
// For non-legacy transactions
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
}
type Header struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase *common.Address `json:"miner" gencodec:"required"`
Root *common.Hash `json:"stateRoot" gencodec:"required"`
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom *types.Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
Number *hexutil.Big `json:"number" gencodec:"required"`
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest *common.Hash `json:"mixHash"`
Nonce *types.BlockNonce `json:"nonce"`
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
// transactions
Transactions []SendTxArgs `json:"transactions"`
}
func (dec *Header) ToHeader() types.Header {
var h types.Header
h.ParentHash = *dec.ParentHash
h.UncleHash = *dec.UncleHash
h.Coinbase = *dec.Coinbase
h.Root = *dec.Root
h.TxHash = *dec.TxHash
h.ReceiptHash = *dec.ReceiptHash
h.Bloom = *dec.Bloom
h.Difficulty = (*big.Int)(dec.Difficulty)
h.Number = (*big.Int)(dec.Number)
h.GasLimit = uint64(*dec.GasLimit)
h.GasUsed = uint64(*dec.GasUsed)
h.Time = uint64(*dec.Time)
h.Extra = *dec.Extra
if dec.MixDigest != nil {
h.MixDigest = *dec.MixDigest
}
if dec.Nonce != nil {
h.Nonce = *dec.Nonce
}
if dec.BaseFee != nil {
h.BaseFee = (*big.Int)(dec.BaseFee)
}
return h
}
// ToTransaction converts the arguments to a transaction.
func (args *SendTxArgs) ToTransaction() *types.Transaction {
// Add the To-field, if specified
var to *common.Address
if args.To != nil {
dstAddr := args.To.Address()
to = &dstAddr
}
var input []byte
if args.Input != nil {
input = *args.Input
} else if args.Data != nil {
input = *args.Data
}
var data types.TxData
switch {
case args.MaxFeePerGas != nil:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.DynamicFeeTx{
To: to,
ChainID: (*big.Int)(args.ChainID),
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
Value: (*big.Int)(&args.Value),
Data: input,
AccessList: al,
}
case args.AccessList != nil:
data = &types.AccessListTx{
To: to,
ChainID: (*big.Int)(args.ChainID),
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasPrice: (*big.Int)(args.GasPrice),
Value: (*big.Int)(&args.Value),
Data: input,
AccessList: *args.AccessList,
}
default:
data = &types.LegacyTx{
To: to,
Nonce: uint64(args.Nonce),
Gas: uint64(args.Gas),
GasPrice: (*big.Int)(args.GasPrice),
Value: (*big.Int)(&args.Value),
Data: input,
}
}
return types.NewTx(data)
}
...@@ -8,12 +8,47 @@ import ( ...@@ -8,12 +8,47 @@ import (
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os" "os"
"strconv"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
var preimages = make(map[common.Hash][]byte) var preimages = make(map[common.Hash][]byte)
var inputs [6]common.Hash
var inputsLoaded bool = false
func Input(index int) common.Hash {
if index < 0 || index > 5 {
panic("bad input index")
}
if !inputsLoaded {
blockNumber, _ := strconv.Atoi(os.Args[1])
f, err := os.Open(fmt.Sprintf("/tmp/eth/%d", blockNumber))
if err != nil {
panic("missing inputs")
}
defer f.Close()
ret, err := ioutil.ReadAll(f)
for i := 0; i < len(inputs); i++ {
inputs[i] = common.BytesToHash(ret[i*0x20 : i*0x20+0x20])
}
inputsLoaded = true
}
return inputs[index]
}
func Output(output common.Hash) {
if output == inputs[5] {
fmt.Println("good transition")
} else {
fmt.Println(output, "!=", inputs[5])
panic("BAD transition :((")
}
}
func Preimage(hash common.Hash) []byte { func Preimage(hash common.Hash) []byte {
val, ok := preimages[hash] val, ok := preimages[hash]
...@@ -43,4 +78,5 @@ func Preimage(hash common.Hash) []byte { ...@@ -43,4 +78,5 @@ func Preimage(hash common.Hash) []byte {
// these are stubs in embedded world // these are stubs in embedded world
func PrefetchStorage(*big.Int, common.Address, common.Hash, func(map[common.Hash][]byte)) {} func PrefetchStorage(*big.Int, common.Address, common.Hash, func(map[common.Hash][]byte)) {}
func PrefetchAccount(*big.Int, common.Address, func(map[common.Hash][]byte)) {} func PrefetchAccount(*big.Int, common.Address, func(map[common.Hash][]byte)) {}
func PrefetchCode(*big.Int, common.Hash) {} func PrefetchCode(blockNumber *big.Int, addrHash common.Hash) {}
func PrefetchBlock(blockNumber *big.Int, startBlock bool, hasher types.TrieHasher) {}
...@@ -16,7 +16,9 @@ import ( ...@@ -16,7 +16,9 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
) )
type jsonreq struct { type jsonreq struct {
...@@ -38,6 +40,12 @@ type jsonresps struct { ...@@ -38,6 +40,12 @@ type jsonresps struct {
Result string `json:"result"` Result string `json:"result"`
} }
type jsonrespt struct {
Jsonrpc string `json:"jsonrpc"`
Id uint64 `json:"id"`
Result Header `json:"result"`
}
// Result structs for GetProof // Result structs for GetProof
type AccountResult struct { type AccountResult struct {
Address common.Address `json:"address"` Address common.Address `json:"address"`
...@@ -169,8 +177,85 @@ func PrefetchCode(blockNumber *big.Int, addrHash common.Hash) { ...@@ -169,8 +177,85 @@ func PrefetchCode(blockNumber *big.Int, addrHash common.Hash) {
preimages[hash] = ret preimages[hash] = ret
} }
func PrefetchBlock(blockNumber *big.Int) { var inputs [6]common.Hash
// TODO: Write this.
func Input(index int) common.Hash {
if index < 0 || index > 5 {
panic("bad input index")
}
return inputs[index]
}
func Output(output common.Hash) {
if output == inputs[5] {
fmt.Println("good transition")
} else {
fmt.Println(output, "!=", inputs[5])
panic("BAD transition :((")
}
}
func PrefetchBlock(blockNumber *big.Int, startBlock bool, hasher types.TrieHasher) {
r := jsonreq{Jsonrpc: "2.0", Method: "eth_getBlockByNumber", Id: 1}
r.Params = make([]interface{}, 2)
r.Params[0] = fmt.Sprintf("0x%x", blockNumber.Int64())
r.Params[1] = true
jsonData, _ := json.Marshal(r)
/*dat, _ := ioutil.ReadAll(getAPI(jsonData))
fmt.Println(string(dat))*/
jr := jsonrespt{}
err := json.NewDecoder(getAPI(jsonData)).Decode(&jr)
if err != nil {
panic("bad block prefetch")
}
//fmt.Println(jr.Result)
blockHeader := jr.Result.ToHeader()
// put in the start block header
if startBlock {
blockHeaderRlp, _ := rlp.EncodeToBytes(blockHeader)
hash := crypto.Keccak256Hash(blockHeaderRlp)
preimages[hash] = blockHeaderRlp
inputs[0] = hash
return
}
// second block
if blockHeader.ParentHash != Input(0) {
fmt.Println(blockHeader.ParentHash, Input(0))
panic("block transition isn't correct")
}
inputs[1] = blockHeader.TxHash
inputs[2] = blockHeader.Coinbase.Hash()
inputs[3] = blockHeader.UncleHash
inputs[4] = common.BigToHash(big.NewInt(int64(blockHeader.GasLimit)))
// secret input
inputs[5] = blockHeader.Root
// save the inputs
saveinput := make([]byte, 0)
for i := 0; i < len(inputs); i++ {
saveinput = append(saveinput, inputs[i].Bytes()[:]...)
}
key := fmt.Sprintf("/tmp/eth/%d", blockNumber.Uint64()-1)
ioutil.WriteFile(key, saveinput, 0644)
txs := make([]*types.Transaction, len(jr.Result.Transactions))
for i := 0; i < len(jr.Result.Transactions); i++ {
txs[i] = jr.Result.Transactions[i].ToTransaction()
}
// put in transaction trie
//return blockHeader, txs
/*var uncles []*types.Header
var receipts []*types.Receipt
block := types.NewBlock(&blockHeader, txs, uncles, receipts, trie.NewStackTrie(nil))*/
} }
func getProofAccount(blockNumber *big.Int, addr common.Address, skey common.Hash, storage bool) []string { func getProofAccount(blockNumber *big.Int, addr common.Address, skey common.Hash, storage bool) []string {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
"dependencies": { "dependencies": {
"@types/node": "^16.9.2", "@types/node": "^16.9.2",
"ethers": "^5.4.7", "ethers": "^5.4.7",
"hardhat": "^2.6.4",
"rlp": "^2.2.6", "rlp": "^2.2.6",
"typescript": "^4.4.3" "typescript": "^4.4.3"
} }
......
...@@ -286,7 +286,7 @@ mu.mem_write(SIZE-0x2000, struct.pack(">IIIIIIIII", ...@@ -286,7 +286,7 @@ mu.mem_write(SIZE-0x2000, struct.pack(">IIIIIIIII",
_AT_PAGESZ, 0x1000, 0)) # auxv _AT_PAGESZ, 0x1000, 0)) # auxv
# block # block
mu.mem_write(SIZE-0x800, b"13284491\x00") mu.mem_write(SIZE-0x800, b"13284469\x00")
#mu.mem_write(SIZE-0x800, b"13284469\x00") #mu.mem_write(SIZE-0x800, b"13284469\x00")
mu.mem_write(SIZE-0x400, b"GOGC=off\x00") mu.mem_write(SIZE-0x400, b"GOGC=off\x00")
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment