Skip to content

Commit

Permalink
cmd/hivechain: support for newer forks, more chain outputs (ethereum#909
Browse files Browse the repository at this point in the history
)
  • Loading branch information
fjl authored and Eikix committed Mar 1, 2024
1 parent 925ab45 commit e88d288
Show file tree
Hide file tree
Showing 33 changed files with 1,522 additions and 414 deletions.
68 changes: 68 additions & 0 deletions cmd/hivechain/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# hivechain

Hivechain creates a non-empty blockchain for testing purposes. To facilitate good tests,
the created chain excercises many protocol features, including:

- different types of transactions
- diverse set of contracts with interesting storage, code, etc.
- contracts to create known log events
- non-transaction chain modifications: coinbase fee payments, uncles, withdrawals...

## Running hivechain

Here is an example command line invocation of the tool:

hivechain generate -fork-interval 6 -tx-interval 1 -length 500 -outdir chain -outputs genesis,chain,headfcu

The command creates a 500-block chain where a new fork gets enabled every six blocks, and
every block contains one 'modification' (i.e. transaction). A number of output files will
be created in the `chain/` directory:

- `genesis.json` contains the genesis block specification
- `chain.rlp` has the blocks in binary RLP format
- `headfcu.json` contains an Engine API request that sends the head block to a client

To see all generator options, run:

hivechain generate -help

## -outputs

Different kinds of output files can be created based on the generated chain. The available
output formats are documented below.

### accounts

Creates `accounts.json` containing accounts and corresponding private keys.

### chain, powchain

`chain` creates `chain.rlp` containing the chain blocks.

`powchain` creates `powchain.rlp` containing only the pre-merge blocks.

### fcu, headfcu, newpayload

`fcu.json` is a JSON array of forkchoiceUpdated requests for all post-merge blocks.

`headfcu.json` is a request for just the head block. This is useful for triggering a sync in the client.

`newpayload.json` is a JSON array of newPayload requests for post-merge blocks.

### genesis

This writes the `genesis.json` file containing a go-ethereum style genesis spec. Note
this file includes the fork block numbers/timestamps.

### headblock

This creates `headblock.json` with a dump of the head header.

### headstate

This writes `headstate.json`, a dump of the complete state of the head block.

### txinfo

The `txinfo.json` file contains an object with a key for each block modifier, and the
value being information about the activity of the modifier.
99 changes: 99 additions & 0 deletions cmd/hivechain/accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"crypto/ecdsa"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

var knownAccounts = []genAccount{
{
key: mustParseKey("4552dbe6ca4699322b5d923d0c9bcdd24644f5db8bf89a085b67c6c49b8a1b91"),
addr: common.HexToAddress("0x7435ed30A8b4AEb0877CEf0c6E8cFFe834eb865f"),
},
{
key: mustParseKey("f6a8f1603b8368f3ca373292b7310c53bec7b508aecacd442554ebc1c5d0c856"),
addr: common.HexToAddress("0x84E75c28348fB86AceA1A93a39426d7D60f4CC46"),
},
{
key: mustParseKey("6e1e16a9c15641c73bf6e237f9293ab1d4e7c12b9adf83cfc94bcf969670f72d"),
addr: common.HexToAddress("0x4ddE844b71bcdf95512Fb4Dc94e84FB67b512eD8"),
},
{
key: mustParseKey("fc39d1c9ddbba176d806ebb42d7460189fe56ca163ad3eb6143bfc6beb6f6f72"),
addr: common.HexToAddress("0xd803681E487E6AC18053aFc5a6cD813c86Ec3E4D"),
},
{
key: mustParseKey("a88293fefc623644969e2ce6919fb0dbd0fd64f640293b4bf7e1a81c97e7fc7f"),
addr: common.HexToAddress("0x4a0f1452281bCec5bd90c3dce6162a5995bfe9df"),
},
{
key: mustParseKey("457075f6822ac29481154792f65c5f1ec335b4fea9ca20f3fea8fa1d78a12c68"),
addr: common.HexToAddress("0x14e46043e63D0E3cdcf2530519f4cFAf35058Cb2"),
},
{
key: mustParseKey("9ee3fd550664b246ad7cdba07162dd25530a3b1d51476dd1d85bbc29f0592684"),
addr: common.HexToAddress("0xE7d13f7Aa2A838D24c59b40186a0aCa1e21CffCC"),
},
{
key: mustParseKey("865898edcf43206d138c93f1bbd86311f4657b057658558888aa5ac4309626a6"),
addr: common.HexToAddress("0x16c57eDF7Fa9D9525378B0b81Bf8A3cEd0620C1c"),
},
{
key: mustParseKey("19168cd7767604b3d19b99dc3da1302b9ccb6ee9ad61660859e07acd4a2625dd"),
addr: common.HexToAddress("0x2D389075BE5be9F2246Ad654cE152cF05990b209"),
},
{
key: mustParseKey("ee7f7875d826d7443ccc5c174e38b2c436095018774248a8074ee92d8914dcdb"),
addr: common.HexToAddress("0x1F4924B14F34e24159387C0A4CdBaa32f3DDb0cF"),
},
{
key: mustParseKey("bfcd0e032489319f4e5ca03e643b2025db624be6cf99cbfed90c4502e3754850"),
addr: common.HexToAddress("0x0c2c51a0990AeE1d73C1228de158688341557508"),
},
{
key: mustParseKey("41be4e00aac79f7ffbb3455053ec05e971645440d594c047cdcc56a3c7458bd6"),
addr: common.HexToAddress("0x5f552da00dFB4d3749D9e62dCeE3c918855A86A0"),
},
{
key: mustParseKey("71aa7d299c7607dabfc3d0e5213d612b5e4a97455b596c2f642daac43fa5eeaa"),
addr: common.HexToAddress("0x3aE75c08b4c907EB63a8960c45B86E1e9ab6123c"),
},
{
key: mustParseKey("c825f31cd8792851e33a290b3d749e553983111fc1f36dfbbdb45f101973f6a9"),
addr: common.HexToAddress("0x654aa64f5FbEFb84c270eC74211B81cA8C44A72e"),
},
{
key: mustParseKey("8d0faa04ae0f9bc3cd4c890aa025d5f40916f4729538b19471c0beefe11d9e19"),
addr: common.HexToAddress("0x717f8AA2b982BeE0e29f573D31Df288663e1Ce16"),
},
{
key: mustParseKey("47f666f20e2175606355acec0ea1b37870c15e5797e962340da7ad7972a537e8"),
addr: common.HexToAddress("0x4340Ee1b812ACB40a1eb561C019c327b243b92Df"),
},
{
key: mustParseKey("8d56bcbcf2c1b7109e1396a28d7a0234e33544ade74ea32c460ce4a443b239b1"),
addr: common.HexToAddress("0xC7B99a164Efd027a93f147376Cc7DA7C67c6bbE0"),
},
{
key: mustParseKey("34391cbbf06956bb506f45ec179cdd84df526aa364e27bbde65db9c15d866d00"),
addr: common.HexToAddress("0x83C7e323d189f18725ac510004fdC2941F8C4A78"),
},
{
key: mustParseKey("25e6ce8611cefb5cd338aeaa9292ed2139714668d123a4fb156cabb42051b5b7"),
addr: common.HexToAddress("0x1F5BDe34B4afC686f136c7a3CB6EC376F7357759"),
},
{
key: mustParseKey("14cdde09d1640eb8c3cda063891b0453073f57719583381ff78811efa6d4199f"),
addr: common.HexToAddress("0xedA8645bA6948855E3B3cD596bbB07596d59c603"),
},
}

func mustParseKey(s string) *ecdsa.PrivateKey {
key, err := crypto.HexToECDSA(s)
if err != nil {
panic(err)
}
return key
}
Binary file added cmd/hivechain/bytecode/callenv.bin
Binary file not shown.
Binary file added cmd/hivechain/bytecode/callme.bin
Binary file not shown.
Binary file added cmd/hivechain/bytecode/callrevert.bin
Binary file not shown.
Binary file added cmd/hivechain/bytecode/deployer.bin
Binary file not shown.
Binary file added cmd/hivechain/bytecode/gencode.bin
Binary file not shown.
Binary file added cmd/hivechain/bytecode/genlogs.bin
Binary file not shown.
1 change: 1 addition & 0 deletions cmd/hivechain/bytecode/genstorage.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
C[��U`Zaa�`W
11 changes: 11 additions & 0 deletions cmd/hivechain/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh

set -xe

geas -bin -no-push0 contracts/deployer.eas > bytecode/deployer.bin
geas -bin -no-push0 contracts/callenv.eas > bytecode/callenv.bin
geas -bin -no-push0 contracts/callme.eas > bytecode/callme.bin
geas -bin -no-push0 contracts/callrevert.eas > bytecode/callrevert.bin
geas -bin -no-push0 contracts/genlogs.eas > bytecode/genlogs.bin
geas -bin -no-push0 contracts/gencode.eas > bytecode/gencode.bin
geas -bin -no-push0 contracts/genstorage.eas > bytecode/genstorage.bin
26 changes: 26 additions & 0 deletions cmd/hivechain/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import _ "embed"

//go:embed bytecode/gencode.bin
var gencodeCode []byte

//go:embed bytecode/genlogs.bin
var genlogsCode []byte

//go:embed bytecode/genstorage.bin
var genstorageCode []byte

//go:embed bytecode/deployer.bin
var deployerCode []byte

//go:embed bytecode/callme.bin
var callmeCode []byte

//go:embed bytecode/callenv.bin
var callenvCode []byte

// //go:embed bytecode/deposit.bin
// var depositCode []byte
//
// const depositContractAddr = "0x00000000219ab540356cBB839Cbe05303d7705Fa"
36 changes: 36 additions & 0 deletions cmd/hivechain/contracts/callenv.eas
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
;;; -*- mode: asm -*-
;;; This contract returns EVM environment data.

#define %store { ; [value, ptr]
dup2 ; [ptr, value, ptr]
mstore ; [ptr]
push 32 ; [32, ptr]
add ; [newptr]
}

.start:
push 0 ; [ptr]

number ; [v, ptr]
%store ; [ptr]

chainid ; [v, ptr]
%store ; [ptr]

coinbase ; [v, ptr]
%store ; [ptr]

basefee ; [v, ptr]
%store ; [ptr]

difficulty ; [v, ptr]
%store ; [ptr]

origin ; [v, ptr]
%store ; [ptr]

callvalue ; [v, ptr]
%store ; [ptr]

push 0 ; [offset, ptr]
return ; []
40 changes: 40 additions & 0 deletions cmd/hivechain/contracts/callme.eas
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
;;; -*- mode: asm -*-
;;; This contract returns value `theOutput` if calldata is exactly equal to `theInput`.
;;; Otherwise, reverts with an error message.

#define theInput 0xff01
#define theOutput 0xffee

#define %revert(error) { ; []
push $error ; [value]
push 0 ; [offset, value]
mstore ; []
push .bytelen($error) ; [size]
push 32-.bytelen($error) ; [offset, size]
revert
}

.start:
calldatasize ; [size]
push .bytelen(theInput) ; [exp, iszero]
eq ; [size==exp]
jumpi @compare ; []
%revert("wrong-calldatasize")

compare:
push 0 ; [offset]
calldataload ; [calldata]
push 256-.bitlen(theInput) ; [shft, calldata]
shr ; [data]
push theInput ; [expv, data]
eq ; [data==expv]
jumpi @return ; []
%revert("wrong-calldata")

return:
push theOutput ; [v]
push 0 ; [offset, v]
mstore ; []
push .bytelen(theOutput) ; [size]
push 32-.bytelen(theInput) ; [offset, size]
return ; []
62 changes: 62 additions & 0 deletions cmd/hivechain/contracts/callrevert.eas
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
;;; -*- mode: asm -*-
;;; This contract is for testing two common Solidity revert encodings:
;;; panic(uint) and error(string).

;;; Dispatch
.start:
push 0 ; [offset]
calldataload ; [word]

;; if word == 0 revert with panic
iszero ; [word==0]
iszero ; [word!=0]
jumpi @error ; [word]

#define s_panic .selector("panic(uint)")
#define panicv 17

;;; Solidity ABI `panic(17)`
;;; Revert data layout:
;;;
;;; selector :: 4 || value :: 32
;;;
.panic:
push s_panic << (28*8) ; [sel]
push 0 ; [offset, sel]
mstore ; []
push 17 ; [panicv]
push 4 ; [offset, panicv]
mstore ; []

push 36 ; [length]
push 0 ; [offset, length]
revert ; []


#define s_error .selector("error(string)")
#define errmsg "user error"
#define errmsg_word errmsg << (255-.bitlen(errmsg))

;;; Solidity ABI error
;;;
;;; Revert data layout:
;;;
;;; selector :: 4 || 0x20 :: 32 || len :: 32 || data :: len
;;;
error:
push s_error << (28*8) ; [sel]
push 0 ; [offset, sel]
mstore ; []
push 0x20 ; [ptr]
push 4 ; [offset, ptr]
mstore ; []
push .bytelen(errmsg) ; [len]
push 36 ; [offset, len]
mstore ; []
push errmsg_word ; [data]
push 68 ; [offset, data]
mstore ; []

push 68 + .bytelen(errmsg) ; [length]
push 0 ; [offset, length]
revert
15 changes: 15 additions & 0 deletions cmd/hivechain/contracts/deployer.eas
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
;;; -*- mode: asm -*-
;;; puts any data after @.end into new contract

push @.end ; [end]
codesize ; [codesize, end]
sub ; [size]
;; copy to memory
dup1 ; [size, size]
push @.end ; [offset, size, size]
push 0 ; [destOffset, offset, size, size]
codecopy ; [size]
;; return memory content
push 0 ; [offset, size]
return ; []
.end:
Loading

0 comments on commit e88d288

Please sign in to comment.