Commit 46930eab authored by protolambda's avatar protolambda Committed by GitHub

op-chain-ops/script: ConsolePrecompile with console2 ABI (#11446)

v2
parent 04757a06
package script
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
type ConsolePrecompile struct {
logger log.Logger
sender common.Address
}
func (c *ConsolePrecompile) log(ctx ...any) {
// frame := c.h.CurrentCall()
// sender := frame.Sender
sender := c.sender
// Log the sender, since the self-address is always equal to the ConsoleAddr
c.logger.With("sender", sender).Info("console", ctx...)
}
//go:generate go run ./consolegen --abi-txt=console2.txt --out=console2_gen.go
This diff is collapsed.
This diff is collapsed.
package script
import (
"log/slog"
"math/rand" // nosemgrep
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/testutils"
)
func TestConsole(t *testing.T) {
logger, captLog := testlog.CaptureLogger(t, log.LevelDebug)
rng := rand.New(rand.NewSource(123))
sender := testutils.RandomAddress(rng)
alice := testutils.RandomAddress(rng)
bob := testutils.RandomAddress(rng)
t.Logf("sender: %s", sender)
t.Logf("alice: %s", alice)
t.Logf("bob: %s", bob)
c := &ConsolePrecompile{
logger: logger,
sender: sender,
}
p, err := NewPrecompile[*ConsolePrecompile](c)
require.NoError(t, err)
// test Log_daf0d4aa
input := make([]byte, 0, 4+32+32)
input = append(input, hexutil.MustDecode("0xdaf0d4aa")...)
input = append(input, leftPad32(alice[:])...)
input = append(input, leftPad32(bob[:])...)
t.Logf("input: %x", input)
_, err = p.Run(input)
require.NoError(t, err)
for i, l := range *captLog.Logs {
t.Logf("log %d", i)
l.Attrs(func(attr slog.Attr) bool {
t.Logf("attr: k: %s, v: %s", attr.Key, attr.Value.String())
return true
})
}
require.NotNil(t, captLog.FindLog(testlog.NewMessageFilter("console")))
require.NotNil(t, captLog.FindLog(testlog.NewAttributesFilter("p0", alice.String())))
require.NotNil(t, captLog.FindLog(testlog.NewAttributesFilter("p1", bob.String())))
require.NotNil(t, captLog.FindLog(testlog.NewAttributesFilter("sender", sender.String())))
}
package main
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/crypto"
)
func main() {
var abiTxtPath string
flag.StringVar(&abiTxtPath, "abi-txt", "console2.txt", "Path of text file with ABI method signatures")
var outPath string
flag.StringVar(&outPath, "out", "console2_gen.go", "Path to write output to")
flag.Parse()
data, err := os.ReadFile(abiTxtPath)
if err != nil {
fmt.Printf("failed to read file: %v\n", err)
os.Exit(1)
}
lines := strings.Split(string(data), "\n")
var out strings.Builder
out.WriteString(`// AUTO-GENERATED - DO NOT EDIT
package script
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
`)
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
byte4ID := crypto.Keccak256([]byte(line))[:4]
if !strings.HasPrefix(line, "log(") {
fmt.Printf("unexpected line: %q\n", line)
os.Exit(1)
}
line = strings.TrimPrefix(line, "log(")
line = strings.TrimSuffix(line, ")")
params := strings.Split(line, ",")
out.WriteString("func (c *ConsolePrecompile) Log_")
out.WriteString(fmt.Sprintf("%x", byte4ID))
out.WriteString("(")
for i, p := range params {
if p == "" {
continue
}
out.WriteString(fmt.Sprintf("p%d ", i))
name, err := solToGo(p)
if err != nil {
fmt.Printf("unexpected param type: %q\n", p)
os.Exit(1)
}
out.WriteString(name)
if i != len(params)-1 {
out.WriteString(", ")
}
}
out.WriteString(") {\n")
out.WriteString("\tc.log(")
for i, p := range params {
if p == "" {
continue
}
out.WriteString(fmt.Sprintf(`"p%d", `, i))
out.WriteString(prettyArg(fmt.Sprintf("p%d", i), p))
if i != len(params)-1 {
out.WriteString(", ")
}
}
out.WriteString(")\n")
out.WriteString("}\n\n")
}
if err := os.WriteFile(outPath, []byte(out.String()), 0644); err != nil {
fmt.Printf("failed to write output: %v\n", err)
os.Exit(1)
}
fmt.Println("done!")
}
func solToGo(name string) (string, error) {
switch name {
case "address":
return "common.Address", nil
case "uint256":
return "*big.Int", nil
case "int256":
return "*ABIInt256", nil
case "string", "bool", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64":
return name, nil
case "bytes":
return "hexutil.Bytes", nil
default:
if strings.HasPrefix(name, "bytes") {
n, err := strconv.Atoi(strings.TrimPrefix(name, "bytes"))
if err != nil {
return "", err
}
if n > 32 {
return "", fmt.Errorf("unexpected large bytes slice type: %d", n)
}
return fmt.Sprintf("[%d]byte", n), nil
}
return "", fmt.Errorf("unrecognized solidity type name: %s", name)
}
}
func prettyArg(arg string, typ string) string {
switch typ {
case "int256":
return fmt.Sprintf("(*big.Int)(%s)", arg)
case "bytes":
return arg
default:
if strings.HasPrefix(typ, "bytes") {
return fmt.Sprintf("hexutil.Bytes(%s[:])", arg)
}
return arg
}
}
......@@ -33,8 +33,17 @@ func b32(v uint64) []byte {
return out
}
// pad to multiple of 32 bytes
func pad32(data []byte) []byte {
// leftPad32 to multiple of 32 bytes
func leftPad32(data []byte) []byte {
out := bytes.Clone(data)
if len(out)%32 == 0 {
return out
}
return append(make([]byte, 32-(len(out)%32)), out...)
}
// rightPad32 to multiple of 32 bytes
func rightPad32(data []byte) []byte {
out := bytes.Clone(data)
if len(out)%32 == 0 {
return out
......@@ -452,7 +461,7 @@ func encodeRevert(outErr error) ([]byte, error) {
out = append(out, revertSelector...) // selector
out = append(out, b32(0x20)...) // offset to string
out = append(out, b32(uint64(len(outErrStr)))...) // length of string
out = append(out, pad32(outErrStr)...) // the error message string
out = append(out, rightPad32(outErrStr)...) // the error message string
return out, vm.ErrExecutionReverted // Geth EVM will pick this up as a revert with return-data
}
......
......@@ -67,7 +67,7 @@ func TestPrecompile(t *testing.T) {
require.Equal(t, e.helloFrom, "alice")
require.Equal(t, out[:32], b32(0x20))
require.Equal(t, out[32:32*2], b32(uint64(len("Hola alice!"))))
require.Equal(t, out[32*2:32*3], pad32([]byte("Hola alice!")))
require.Equal(t, out[32*2:32*3], rightPad32([]byte("Hola alice!")))
// error handling
input = crypto.Keccak256([]byte("greet(string)"))[:4]
......@@ -102,12 +102,12 @@ func TestPrecompile(t *testing.T) {
out, err = p.Run(input)
require.NoError(t, err)
require.Equal(t, b32(123), out[:32])
require.Equal(t, b32(32*3), out[32*1:32*2]) // offset of hello
require.Equal(t, b32(32*5), out[32*2:32*3]) // offset of seen
require.Equal(t, b32(uint64(len("Hola"))), out[32*3:32*4]) // length of hello
require.Equal(t, pad32([]byte("Hola")), out[32*4:32*5]) // hello content
require.Equal(t, b32(uint64(len("alice"))), out[32*5:32*6]) // length of seen
require.Equal(t, pad32([]byte("alice")), out[32*6:32*7]) // seen content
require.Equal(t, b32(32*3), out[32*1:32*2]) // offset of hello
require.Equal(t, b32(32*5), out[32*2:32*3]) // offset of seen
require.Equal(t, b32(uint64(len("Hola"))), out[32*3:32*4]) // length of hello
require.Equal(t, rightPad32([]byte("Hola")), out[32*4:32*5]) // hello content
require.Equal(t, b32(uint64(len("alice"))), out[32*5:32*6]) // length of seen
require.Equal(t, rightPad32([]byte("alice")), out[32*6:32*7]) // seen content
// multi-input
input = crypto.Keccak256([]byte("addAndMul(uint64,uint64,uint64,uint8)"))[:4]
......
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