diff --git a/geth-utils/README.md b/geth-utils/README.md index 91e2040dba..f5961c529a 100644 --- a/geth-utils/README.md +++ b/geth-utils/README.md @@ -2,19 +2,6 @@ The module `gethutil` tried to provide identical output from APIs `debug_trace*` of latest `geth` as test vectors for [`zkevm-circuits`](https://github.com/privacy-scaling-explorations/zkevm-circuits). -## Usage - - - - -### Library Usage - -For [`./example/mstore_mload.go`](./example/mstore_mload.go) as an example, it defines bytecode directly by builder `asm`, then write the logs produced by `TraceTx` to stdout. To reproduce the logs, run: - -```bash -go run ./example/mstore_mload.go > ./mstore_mload.json -``` - ### Debugging The execution traces returned by geth omit some information like execution diff --git a/geth-utils/build.rs b/geth-utils/build.rs index 7bf64f596a..396de6484e 100644 --- a/geth-utils/build.rs +++ b/geth-utils/build.rs @@ -26,12 +26,7 @@ fn main() { } // Files the lib depends on that should recompile the lib - let dep_files = vec![ - "./gethutil/asm.go", - "./gethutil/trace.go", - "./gethutil/util.go", - "./go.mod", - ]; + let dep_files = vec!["./gethutil/trace.go", "./go.mod"]; for file in dep_files { println!("cargo:rerun-if-changed={}", file); } diff --git a/geth-utils/example/add_sub.go b/geth-utils/example/add_sub.go deleted file mode 100644 index d58a3e033a..0000000000 --- a/geth-utils/example/add_sub.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/ethereum/go-ethereum/common" - - "main/gethutil" -) - -func main() { - address := common.BytesToAddress([]byte{0xff}) - assembly := gethutil.NewAssembly().Add(0xdeadbeef, 0xcafeb0ba).Sub(0xfaceb00c, 0xb0bacafe) - - accounts := map[common.Address]gethutil.Account{address: {Code: assembly.Bytecode()}} - tx := gethutil.Transaction{To: &address, GasLimit: 21100} - - result, err := gethutil.Trace(gethutil.TraceConfig{Accounts: accounts, Transactions: []gethutil.Transaction{tx}}) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to trace tx, err: %v\n", err) - } - - bytes, err := json.MarshalIndent(result[0].StructLogs, "", " ") - if err != nil { - fmt.Fprintf(os.Stderr, "failed to marshal logs, err: %v\n", err) - } - - fmt.Fprintln(os.Stdout, string(bytes)) -} diff --git a/geth-utils/example/msize.go b/geth-utils/example/msize.go deleted file mode 100644 index 9c13f85d76..0000000000 --- a/geth-utils/example/msize.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/ethereum/go-ethereum/common" - - "main/gethutil" -) - -func main() { - address := common.BytesToAddress([]byte{0xff}) - assembly := gethutil.NewAssembly().MStore(0x40, 0x80).MSize().Stop() - - accounts := map[common.Address]gethutil.Account{address: {Code: assembly.Bytecode()}} - tx := gethutil.Transaction{To: &address, GasLimit: 21100} - - result, err := gethutil.Trace(gethutil.TraceConfig{Accounts: accounts, Transactions: []gethutil.Transaction{tx}}) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to trace tx, err: %v\n", err) - } - - bytes, err := json.MarshalIndent(result[0].StructLogs, "", " ") - if err != nil { - fmt.Fprintf(os.Stderr, "failed to marshal logs, err: %v\n", err) - } - - fmt.Fprintln(os.Stdout, string(bytes)) -} diff --git a/geth-utils/example/mstore_mload.go b/geth-utils/example/mstore_mload.go deleted file mode 100644 index ecfc6d91b5..0000000000 --- a/geth-utils/example/mstore_mload.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/ethereum/go-ethereum/common" - - "main/gethutil" -) - -func main() { - address := common.BytesToAddress([]byte{0xff}) - assembly := gethutil.NewAssembly().MStore(0x40, 0x80).MLoad(0x40) - - accounts := map[common.Address]gethutil.Account{address: {Code: assembly.Bytecode()}} - tx := gethutil.Transaction{To: &address, GasLimit: 21100} - - result, err := gethutil.Trace(gethutil.TraceConfig{Accounts: accounts, Transactions: []gethutil.Transaction{tx}}) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to trace tx, err: %v\n", err) - } - - bytes, err := json.MarshalIndent(result[0].StructLogs, "", " ") - if err != nil { - fmt.Fprintf(os.Stderr, "failed to marshal logs, err: %v\n", err) - } - - fmt.Fprintln(os.Stdout, string(bytes)) -} diff --git a/geth-utils/example/sstore_sload.go b/geth-utils/example/sstore_sload.go deleted file mode 100644 index b3bc8a672e..0000000000 --- a/geth-utils/example/sstore_sload.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/ethereum/go-ethereum/common" - - "main/gethutil" -) - -func main() { - address := common.BytesToAddress([]byte{0xff}) - assembly := gethutil.NewAssembly().SStore(0, 0xcafeb0ba).SLoad(0).SStore(0, 0xdeabbeef).SLoad(0) - - accounts := map[common.Address]gethutil.Account{address: {Code: assembly.Bytecode()}} - tx := gethutil.Transaction{To: &address, GasLimit: 46000} - - result, err := gethutil.Trace(gethutil.TraceConfig{Accounts: accounts, Transactions: []gethutil.Transaction{tx}}) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to trace tx, err: %v\n", err) - } - - bytes, err := json.MarshalIndent(result[0].StructLogs, "", " ") - if err != nil { - fmt.Fprintf(os.Stderr, "failed to marshal logs, err: %v\n", err) - } - - fmt.Fprintln(os.Stdout, string(bytes)) -} diff --git a/geth-utils/gethutil/asm.go b/geth-utils/gethutil/asm.go deleted file mode 100644 index b5546f876a..0000000000 --- a/geth-utils/gethutil/asm.go +++ /dev/null @@ -1,231 +0,0 @@ -package gethutil - -import ( - "fmt" - "io" - "math/big" - "strings" - - "github.com/ethereum/go-ethereum/core/vm" -) - -type Asm struct { - bytecode []byte - labelMap map[string]int - pendingLabelsMap map[string][]int -} - -func NewAssembly() *Asm { - return &Asm{ - labelMap: make(map[string]int), - pendingLabelsMap: make(map[string][]int), - } -} - -func (a *Asm) Bytecode() []byte { - if len(a.pendingLabelsMap) > 0 { - pendingLabels := make([]string, 0, len(a.pendingLabelsMap)) - for pendingLabel := range a.pendingLabelsMap { - pendingLabels = append(pendingLabels, pendingLabel) - } - panic(fmt.Sprintf("pending labels are not defined yet: %v", strings.Join(pendingLabels, ", "))) - } - return a.bytecode -} - -func (a *Asm) PrintMnemonics(out io.Writer) { - for idx := 0; idx < len(a.bytecode); { - code := vm.OpCode(a.bytecode[idx]) - if code.IsPush() { - n := int(code) - int(vm.PUSH1) + 1 - fmt.Fprintf(out, "%02d\t%s\t0x%x\n", idx, code.String(), a.bytecode[idx+1:idx+1+n]) - idx += n + 1 - } else { - fmt.Fprintf(out, "%02d\t%s\n", idx, code.String()) - idx++ - } - } -} - -// 0x0 range -func (a *Asm) Stop() *Asm { return a.appendByte(vm.STOP) } -func (a *Asm) Add(v ...interface{}) *Asm { return a.opWithPush(vm.ADD, v...) } -func (a *Asm) Mul(v ...interface{}) *Asm { return a.opWithPush(vm.MUL, v...) } -func (a *Asm) Sub(v ...interface{}) *Asm { return a.opWithPush(vm.SUB, v...) } -func (a *Asm) Div(v ...interface{}) *Asm { return a.opWithPush(vm.DIV, v...) } -func (a *Asm) SDiv(v ...interface{}) *Asm { return a.opWithPush(vm.SDIV, v...) } -func (a *Asm) Mod(v ...interface{}) *Asm { return a.opWithPush(vm.MOD, v...) } -func (a *Asm) SMod(v ...interface{}) *Asm { return a.opWithPush(vm.SMOD, v...) } -func (a *Asm) AddMod(v ...interface{}) *Asm { return a.opWithPush(vm.ADDMOD, v...) } -func (a *Asm) MulMod(v ...interface{}) *Asm { return a.opWithPush(vm.MULMOD, v...) } -func (a *Asm) Exp(v ...interface{}) *Asm { return a.opWithPush(vm.EXP, v...) } -func (a *Asm) SignExtend(v ...interface{}) *Asm { return a.opWithPush(vm.SIGNEXTEND, v...) } - -// 0x10 range -func (a *Asm) Lt(v ...interface{}) *Asm { return a.opWithPush(vm.LT, v...) } -func (a *Asm) Gt(v ...interface{}) *Asm { return a.opWithPush(vm.GT, v...) } -func (a *Asm) SLt(v ...interface{}) *Asm { return a.opWithPush(vm.SLT, v...) } -func (a *Asm) SGt(v ...interface{}) *Asm { return a.opWithPush(vm.SGT, v...) } -func (a *Asm) Eq(v ...interface{}) *Asm { return a.opWithPush(vm.EQ, v...) } -func (a *Asm) IsZero(v ...interface{}) *Asm { return a.opWithPush(vm.ISZERO, v...) } -func (a *Asm) And(v ...interface{}) *Asm { return a.opWithPush(vm.AND, v...) } -func (a *Asm) Or(v ...interface{}) *Asm { return a.opWithPush(vm.OR, v...) } -func (a *Asm) Xor(v ...interface{}) *Asm { return a.opWithPush(vm.XOR, v...) } -func (a *Asm) Not(v ...interface{}) *Asm { return a.opWithPush(vm.NOT, v...) } -func (a *Asm) Byte(v ...interface{}) *Asm { return a.opWithPush(vm.BYTE, v...) } -func (a *Asm) Shl(v ...interface{}) *Asm { return a.opWithPush(vm.SHL, v...) } -func (a *Asm) Shr(v ...interface{}) *Asm { return a.opWithPush(vm.SHR, v...) } -func (a *Asm) Sar(v ...interface{}) *Asm { return a.opWithPush(vm.SAR, v...) } -func (a *Asm) Sha3(v ...interface{}) *Asm { return a.opWithPush(vm.KECCAK256, v...) } - -// 0x30 range -func (a *Asm) Address() *Asm { return a.appendByte(vm.ADDRESS) } -func (a *Asm) Balance(v ...interface{}) *Asm { return a.opWithPush(vm.BALANCE, v...) } -func (a *Asm) Origin() *Asm { return a.appendByte(vm.ORIGIN) } -func (a *Asm) Caller() *Asm { return a.appendByte(vm.CALLER) } -func (a *Asm) CallValue() *Asm { return a.appendByte(vm.CALLVALUE) } -func (a *Asm) CallDataLoad(v ...interface{}) *Asm { return a.opWithPush(vm.CALLDATALOAD, v...) } -func (a *Asm) CallDataSize() *Asm { return a.appendByte(vm.CALLDATASIZE) } -func (a *Asm) CallDataCopy(v ...interface{}) *Asm { return a.opWithPush(vm.CALLDATACOPY, v...) } -func (a *Asm) CodeSize() *Asm { return a.appendByte(vm.CODESIZE) } -func (a *Asm) CodeCopy(v ...interface{}) *Asm { return a.opWithPush(vm.CODECOPY, v...) } -func (a *Asm) GasPrice() *Asm { return a.appendByte(vm.GASPRICE) } -func (a *Asm) ExtCodeSize(v ...interface{}) *Asm { return a.opWithPush(vm.EXTCODESIZE, v...) } -func (a *Asm) ExtCodeCopy(v ...interface{}) *Asm { return a.opWithPush(vm.EXTCODECOPY, v...) } -func (a *Asm) ReturnDataSize() *Asm { return a.appendByte(vm.RETURNDATASIZE) } -func (a *Asm) ReturnDataCopy(v ...interface{}) *Asm { return a.opWithPush(vm.RETURNDATACOPY, v...) } -func (a *Asm) ExtCodeHash(v ...interface{}) *Asm { return a.opWithPush(vm.EXTCODEHASH, v...) } - -// 0x40 range -func (a *Asm) BlockHash(v ...interface{}) *Asm { return a.opWithPush(vm.BLOCKHASH, v...) } -func (a *Asm) Coinbase() *Asm { return a.appendByte(vm.COINBASE) } -func (a *Asm) Timestamp() *Asm { return a.appendByte(vm.TIMESTAMP) } -func (a *Asm) Number() *Asm { return a.appendByte(vm.NUMBER) } -func (a *Asm) Difficulty() *Asm { return a.appendByte(vm.DIFFICULTY) } -func (a *Asm) GasLimit() *Asm { return a.appendByte(vm.GASLIMIT) } -func (a *Asm) ChainID() *Asm { return a.appendByte(vm.CHAINID) } -func (a *Asm) SelfBalance() *Asm { return a.appendByte(vm.SELFBALANCE) } -func (a *Asm) BaseFee() *Asm { return a.appendByte(vm.BASEFEE) } - -// 0x50 range -func (a *Asm) Pop() *Asm { return a.appendByte(vm.POP) } -func (a *Asm) MLoad(v ...interface{}) *Asm { return a.opWithPush(vm.MLOAD, v...) } -func (a *Asm) MStore(v ...interface{}) *Asm { return a.opWithPush(vm.MSTORE, v...) } -func (a *Asm) MStore8(v ...interface{}) *Asm { return a.opWithPush(vm.MSTORE8, v...) } -func (a *Asm) SLoad(v ...interface{}) *Asm { return a.opWithPush(vm.SLOAD, v...) } -func (a *Asm) SStore(v ...interface{}) *Asm { return a.opWithPush(vm.SSTORE, v...) } -func (a *Asm) Jump(label ...string) *Asm { return a.jump(vm.JUMP, label...) } -func (a *Asm) JumpI(label ...string) *Asm { return a.jump(vm.JUMPI, label...) } -func (a *Asm) PC() *Asm { return a.appendByte(vm.PC) } -func (a *Asm) MSize() *Asm { return a.appendByte(vm.MSIZE) } -func (a *Asm) Gas() *Asm { return a.appendByte(vm.GAS) } -func (a *Asm) JumpDest(label ...string) *Asm { return a.jumpDest(label...) } - -// 0x60 range -func (a *Asm) PushX(val interface{}) *Asm { return a.push(val) } -func (a *Asm) DupX(x int) *Asm { - rangeCheck(x, 1, 16, "X") - return a.appendByte(int(vm.DUP1) + x - 1) -} -func (a *Asm) SwapX(x int) *Asm { - rangeCheck(x, 1, 16, "X") - return a.appendByte(int(vm.SWAP1) + x - 1) -} - -// 0xa0 range -func (a *Asm) LogX(x int) *Asm { - rangeCheck(x, 0, 5, "X") - return a.appendByte(int(vm.LOG0) + x) -} - -// 0xf0 range -func (a *Asm) Create(v ...interface{}) *Asm { return a.opWithPush(vm.CREATE, v...) } -func (a *Asm) Call(v ...interface{}) *Asm { return a.opWithPush(vm.CALL, v...) } -func (a *Asm) CallCode(v ...interface{}) *Asm { return a.opWithPush(vm.CALLCODE, v...) } -func (a *Asm) Return(v ...interface{}) *Asm { return a.opWithPush(vm.RETURN, v...) } -func (a *Asm) DelegateCall(v ...interface{}) *Asm { return a.opWithPush(vm.DELEGATECALL, v...) } -func (a *Asm) Create2(v ...interface{}) *Asm { return a.opWithPush(vm.CREATE2, v...) } -func (a *Asm) StaticCall(v ...interface{}) *Asm { return a.opWithPush(vm.STATICCALL, v...) } -func (a *Asm) Revert(v ...interface{}) *Asm { return a.opWithPush(vm.REVERT, v...) } -func (a *Asm) SelfDestruct() *Asm { return a.appendByte(vm.SELFDESTRUCT) } - -func (a *Asm) jump(op vm.OpCode, label ...string) *Asm { - if len(label) > 0 { - rangeCheck(len(label), 1, 1, "len(label)") - - if pos, ok := a.labelMap[label[0]]; ok { - a.PushX(pos) - } else { - a.pendingLabelsMap[label[0]] = append(a.pendingLabelsMap[label[0]], len(a.bytecode)) - a.PushX([]byte{0, 0, 0}) - } - } - - return a.appendByte(op) -} - -func (a *Asm) jumpDest(label ...string) *Asm { - a.appendByte(vm.JUMPDEST) - - if len(label) > 0 { - rangeCheck(len(label), 1, 1, "len(label)") - - if _, ok := a.labelMap[label[0]]; ok { - panic("label already defined") - } - - a.labelMap[label[0]] = len(a.bytecode) - - pos := big.NewInt(int64(len(a.bytecode) - 1)).Bytes() - if len(pos) < 3 { - pos = append(make([]byte, 3-len(pos)), pos...) - } - for _, pendingLabel := range a.pendingLabelsMap[label[0]] { - copy(a.bytecode[pendingLabel+1:pendingLabel+4], pos) - } - - delete(a.pendingLabelsMap, label[0]) - } - - return a -} - -func (a *Asm) opWithPush(op vm.OpCode, v ...interface{}) *Asm { - opPushRangeCheck(op, len(v)) - return a.pushRev(v...).appendByte(op) -} - -func (a *Asm) push(v ...interface{}) *Asm { - for _, v := range v { - bytes := toBytes(v) - - rangeCheck(len(bytes), 1, 32, "len(bytes)") - a.appendByte(int(vm.PUSH1) + len(bytes) - 1) - - for _, b := range bytes { - a.appendByte(b) - } - } - - return a -} - -func (a *Asm) pushRev(v ...interface{}) *Asm { - reverse(v) - return a.push(v...) -} - -func (a *Asm) appendByte(v interface{}) *Asm { - switch v := v.(type) { - case vm.OpCode: - a.bytecode = append(a.bytecode, byte(v)) - case byte: - a.bytecode = append(a.bytecode, v) - case int: - a.bytecode = append(a.bytecode, byte(v)) - default: - panic(fmt.Sprintf("unexpected appendByte type %T", v)) - } - - return a -} diff --git a/geth-utils/gethutil/trace.go b/geth-utils/gethutil/trace.go index d8ddc32114..185df65b0f 100644 --- a/geth-utils/gethutil/trace.go +++ b/geth-utils/gethutil/trace.go @@ -126,6 +126,13 @@ type TraceConfig struct { func newUint64(val uint64) *uint64 { return &val } +func toBigInt(value *hexutil.Big) *big.Int { + if value != nil { + return value.ToInt() + } + return big.NewInt(0) +} + func Trace(config TraceConfig) ([]*ExecutionResult, error) { chainConfig := params.ChainConfig{ ChainID: toBigInt(config.ChainID), @@ -164,16 +171,16 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { txAccessList[i].StorageKeys = accessList.StorageKeys } messages[i] = core.Message{ - From: tx.From, - To: tx.To, - Nonce: uint64(tx.Nonce), - Value: toBigInt(tx.Value), - GasLimit: uint64(tx.GasLimit), - GasPrice: toBigInt(tx.GasPrice), - GasFeeCap: toBigInt(tx.GasFeeCap), - GasTipCap: toBigInt(tx.GasTipCap), - Data: tx.CallData, - AccessList: txAccessList, + From: tx.From, + To: tx.To, + Nonce: uint64(tx.Nonce), + Value: toBigInt(tx.Value), + GasLimit: uint64(tx.GasLimit), + GasPrice: toBigInt(tx.GasPrice), + GasFeeCap: toBigInt(tx.GasFeeCap), + GasTipCap: toBigInt(tx.GasTipCap), + Data: tx.CallData, + AccessList: txAccessList, SkipAccountChecks: false, } diff --git a/geth-utils/gethutil/util.go b/geth-utils/gethutil/util.go deleted file mode 100644 index 374c9fa529..0000000000 --- a/geth-utils/gethutil/util.go +++ /dev/null @@ -1,85 +0,0 @@ -package gethutil - -import ( - "fmt" - "math/big" - "unsafe" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/holiman/uint256" -) - -var ( - minStackPtrOffset = 3 * unsafe.Sizeof(int(0)) - longonInstructionSet = newLondonInstructionSet() -) - -//go:linkname newLondonInstructionSet github.com/ethereum/go-ethereum/core/vm.newLondonInstructionSet -func newLondonInstructionSet() vm.JumpTable - -func opPushRangeCheck(op vm.OpCode, n int) { - opPtr := unsafe.Pointer(longonInstructionSet[op]) - minStack := *(*int)(unsafe.Pointer(uintptr(opPtr) + minStackPtrOffset)) - rangeCheck(n, 0, minStack, fmt.Sprintf("len(vals) of %s", op.String())) -} - -func rangeCheck(n, l, h int, name string) { - if n < l || n > h { - if l == h { - panic(fmt.Sprintf("%s should be %d, but got %d", name, h, n)) - } else { - panic(fmt.Sprintf("%s should be in range [%d, %d], but got %d", name, l, h, n)) - } - } -} - -func reverse(vals []interface{}) { - for i, j := 0, len(vals)-1; i < j; i, j = i+1, j-1 { - vals[i], vals[j] = vals[j], vals[i] - } -} - -func toBytes(value interface{}) []byte { - var bytes []byte - - switch value := value.(type) { - case string: - u256, err := uint256.FromHex(value) - if err != nil { - panic(err) - } - bytes = u256.Bytes() - case int: - for value > 0 { - bytes = append([]byte{byte(value & 0xff)}, bytes...) - value >>= 8 - } - case []byte: - bytes = value - case common.Address: - bytes = value.Bytes() - case common.Hash: - bytes = value.Bytes() - case *uint256.Int: - bytes = value.Bytes() - case *big.Int: - bytes = value.Bytes() - default: - panic(fmt.Errorf("Unsupported type %T", value)) - } - - if len(bytes) == 0 { - bytes = []byte{0} - } - - return bytes -} - -func toBigInt(value *hexutil.Big) *big.Int { - if value != nil { - return value.ToInt() - } - return big.NewInt(0) -} diff --git a/geth-utils/go.mod b/geth-utils/go.mod index 2892aa855f..8b468f2c19 100644 --- a/geth-utils/go.mod +++ b/geth-utils/go.mod @@ -2,10 +2,7 @@ module main go 1.16 -require ( - github.com/ethereum/go-ethereum v1.11.5 - github.com/holiman/uint256 v1.2.0 -) +require github.com/ethereum/go-ethereum v1.11.5 // Uncomment for debugging // replace github.com/ethereum/go-ethereum => ../../go-ethereum