From b6d5cdbc6cf064e61e22726b8f1ed3b584e1c5e3 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Wed, 19 Feb 2025 10:29:57 +0000 Subject: [PATCH 01/15] chore: log registration of extras + precompile overriding --- core/types/rlp_payload.libevm.go | 7 +++++++ core/vm/evm.go | 2 ++ log/value.libevm.go | 35 ++++++++++++++++++++++++++++++++ params/config.libevm.go | 7 +++++++ 4 files changed, 51 insertions(+) create mode 100644 log/value.libevm.go diff --git a/core/types/rlp_payload.libevm.go b/core/types/rlp_payload.libevm.go index 98aebe1cc4b..926a5b3cab8 100644 --- a/core/types/rlp_payload.libevm.go +++ b/core/types/rlp_payload.libevm.go @@ -23,6 +23,7 @@ import ( "github.com/ava-labs/libevm/libevm/pseudo" "github.com/ava-labs/libevm/libevm/register" "github.com/ava-labs/libevm/libevm/testonly" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/rlp" ) @@ -84,6 +85,12 @@ func RegisterExtras[ newStateAccount: pseudo.NewConstructor[SA]().Zero, hooks: extra, }) + log.Debug( + "Registered core/types extras", + "Header", log.TypeOf(pseudo.Zero[HPtr]().Value.Get()), + "Block/Body", log.TypeOf(pseudo.Zero[BPtr]().Value.Get()), + "StateAccount", log.TypeOf(pseudo.Zero[SA]().Value.Get()), + ) return extra } diff --git a/core/vm/evm.go b/core/vm/evm.go index ae56d5a69ed..200de88f132 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -24,6 +24,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" ) @@ -40,6 +41,7 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { if p, override := evm.chainRules.Hooks().PrecompileOverride(addr); override { + log.Debug("Overriding precompile", "Address", addr, "Implementation", log.TypeOf(p)) return p, p != nil } var precompiles map[common.Address]PrecompiledContract diff --git a/log/value.libevm.go b/log/value.libevm.go new file mode 100644 index 00000000000..974eb45e5ea --- /dev/null +++ b/log/value.libevm.go @@ -0,0 +1,35 @@ +// Copyright 2025 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +package log + +import ( + "fmt" + + "golang.org/x/exp/slog" +) + +// TypeOf returns a LogValuer that reports the concrete type of `v` as +// determined with the `%T` [fmt] verb. +func TypeOf(v any) slog.LogValuer { + return concreteTypeValue{v} +} + +type concreteTypeValue struct{ v any } + +func (v concreteTypeValue) LogValue() slog.Value { + return slog.StringValue(fmt.Sprintf("%T", v.v)) +} diff --git a/params/config.libevm.go b/params/config.libevm.go index f8a153f3d98..255826a4c31 100644 --- a/params/config.libevm.go +++ b/params/config.libevm.go @@ -23,6 +23,7 @@ import ( "github.com/ava-labs/libevm/libevm/pseudo" "github.com/ava-labs/libevm/libevm/register" + "github.com/ava-labs/libevm/log" ) // Extras are arbitrary payloads to be added as extra fields in [ChainConfig] @@ -79,6 +80,12 @@ func RegisterExtras[C ChainConfigHooks, R RulesHooks](e Extras[C, R]) ExtraPaylo newForRules: e.newForRules, payloads: payloads, }) + log.Debug( + "Registered params extras", + "ChainConfig", log.TypeOf(pseudo.Zero[C]().Value.Get()), + "Rules", log.TypeOf(pseudo.Zero[R]().Value.Get()), + "ReuseJSONRoot", e.ReuseJSONRoot, + ) return payloads } From a8e6668cbfe134f613f8559a51833405ce167a4e Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Thu, 20 Feb 2025 11:58:45 +0000 Subject: [PATCH 02/15] test: `log.TypeOf()` --- log/value.libevm_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 log/value.libevm_test.go diff --git a/log/value.libevm_test.go b/log/value.libevm_test.go new file mode 100644 index 00000000000..1e68b0dd67c --- /dev/null +++ b/log/value.libevm_test.go @@ -0,0 +1,40 @@ +// Copyright 2025 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTypeOf(t *testing.T) { + type foo struct{} + + tests := map[any]string{ + nil: "", + int(0): "int", + int(1): "int", + uint(0): "uint", + foo{}: "log.foo", + } + + for in, want := range tests { + got := TypeOf(in).LogValue() + assert.Equalf(t, want, got.String(), "TypeOf(%T(%[1]v))", in, in) + } +} From 28c96d4c96fb9a62d36ef4e80d5200a6b8b8a775 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Thu, 20 Feb 2025 11:59:51 +0000 Subject: [PATCH 03/15] refactor: log type registration at `Info` --- core/types/rlp_payload.libevm.go | 2 +- params/config.libevm.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/types/rlp_payload.libevm.go b/core/types/rlp_payload.libevm.go index 926a5b3cab8..ff3530d1692 100644 --- a/core/types/rlp_payload.libevm.go +++ b/core/types/rlp_payload.libevm.go @@ -85,7 +85,7 @@ func RegisterExtras[ newStateAccount: pseudo.NewConstructor[SA]().Zero, hooks: extra, }) - log.Debug( + log.Info( "Registered core/types extras", "Header", log.TypeOf(pseudo.Zero[HPtr]().Value.Get()), "Block/Body", log.TypeOf(pseudo.Zero[BPtr]().Value.Get()), diff --git a/params/config.libevm.go b/params/config.libevm.go index 255826a4c31..127f5febef0 100644 --- a/params/config.libevm.go +++ b/params/config.libevm.go @@ -80,7 +80,7 @@ func RegisterExtras[C ChainConfigHooks, R RulesHooks](e Extras[C, R]) ExtraPaylo newForRules: e.newForRules, payloads: payloads, }) - log.Debug( + log.Info( "Registered params extras", "ChainConfig", log.TypeOf(pseudo.Zero[C]().Value.Get()), "Rules", log.TypeOf(pseudo.Zero[R]().Value.Get()), From 9ffcbdbb2185bab0f1e457e8be3e93ab5daeae93 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Thu, 20 Feb 2025 12:03:18 +0000 Subject: [PATCH 04/15] test: `TypeOf([typed nil])` --- log/value.libevm_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/log/value.libevm_test.go b/log/value.libevm_test.go index 1e68b0dd67c..eee1c4c9ea9 100644 --- a/log/value.libevm_test.go +++ b/log/value.libevm_test.go @@ -26,11 +26,12 @@ func TestTypeOf(t *testing.T) { type foo struct{} tests := map[any]string{ - nil: "", - int(0): "int", - int(1): "int", - uint(0): "uint", - foo{}: "log.foo", + nil: "", + int(0): "int", + int(1): "int", + uint(0): "uint", + foo{}: "log.foo", + (*foo)(nil): "*log.foo", } for in, want := range tests { From 62111405d250a4521fbe377d67741edeadea8e13 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Fri, 21 Feb 2025 09:52:28 +0000 Subject: [PATCH 05/15] feat(log): `type Lazy func() slog.Value` --- log/value.libevm.go | 10 ++++++++++ log/value.libevm_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/log/value.libevm.go b/log/value.libevm.go index 974eb45e5ea..7f8034e79d7 100644 --- a/log/value.libevm.go +++ b/log/value.libevm.go @@ -33,3 +33,13 @@ type concreteTypeValue struct{ v any } func (v concreteTypeValue) LogValue() slog.Value { return slog.StringValue(fmt.Sprintf("%T", v.v)) } + +// A Lazy function defers its execution until and if logging is performed. +type Lazy func() slog.Value + +var _ slog.LogValuer = Lazy(nil) + +// LogValue implements the [slog.LogValuer] interface. +func (l Lazy) LogValue() slog.Value { + return l() +} diff --git a/log/value.libevm_test.go b/log/value.libevm_test.go index eee1c4c9ea9..de1005d0d48 100644 --- a/log/value.libevm_test.go +++ b/log/value.libevm_test.go @@ -17,9 +17,11 @@ package log import ( + "bytes" "testing" "github.com/stretchr/testify/assert" + "golang.org/x/exp/slog" ) func TestTypeOf(t *testing.T) { @@ -39,3 +41,27 @@ func TestTypeOf(t *testing.T) { assert.Equalf(t, want, got.String(), "TypeOf(%T(%[1]v))", in, in) } } + +func TestLazy(t *testing.T) { + const ( + key = "theKey" + val = "theVal" + wantLogged = key + "=" + val + ) + + var gotNumEvaluations int + fn := Lazy(func() slog.Value { + gotNumEvaluations++ + return slog.StringValue(val) + }) + + var out bytes.Buffer + log := slog.New(slog.NewTextHandler(&out, &slog.HandlerOptions{ + Level: slog.LevelInfo, + })) + log.Info("", key, fn) + log.Debug("", "not evaluated", fn) + + assert.Contains(t, out.String(), wantLogged, "evaluation of lazy function is logged") + assert.Equalf(t, 1, gotNumEvaluations, "number of evaluations of %T function", fn) +} From 9e862fa2b3dd156b331a4a3e300ae29e9f7f8315 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Fri, 21 Feb 2025 11:34:46 +0000 Subject: [PATCH 06/15] chore: lazily log differences in active precompiles --- core/vm/contracts.go | 5 +---- core/vm/contracts.libevm.go | 29 +++++++++++++++++++++++++++++ go.mod | 3 ++- go.sum | 7 +++++-- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index a869debebba..ff1f3a0ca79 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -148,10 +148,7 @@ func init() { } // ActivePrecompiles returns the precompiles enabled with the current configuration. -func ActivePrecompiles(rules params.Rules) (active []common.Address) { - defer func() { - active = rules.Hooks().ActivePrecompiles(append([]common.Address{}, active...)) - }() +func activePrecompiles(rules params.Rules) []common.Address { switch { case rules.IsCancun: return PrecompiledAddressesCancun diff --git a/core/vm/contracts.libevm.go b/core/vm/contracts.libevm.go index 66e3c91783a..60cf9962921 100644 --- a/core/vm/contracts.libevm.go +++ b/core/vm/contracts.libevm.go @@ -20,14 +20,43 @@ import ( "fmt" "math/big" + set "github.com/hashicorp/go-set/v3" "github.com/holiman/uint256" + "golang.org/x/exp/slog" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/params" ) +// ActivePrecompiles returns the precompiles enabled with the current configuration. +func ActivePrecompiles(rules params.Rules) []common.Address { + orig := activePrecompiles(rules) // original, upstream implementation + active := rules.Hooks().ActivePrecompiles(append([]common.Address{}, orig...)) + + // As all set computation is done lazily and only when debugging, there is + // some duplication in favour of simplified code. + log.Debug( + "Overriding active precompiles", + "Added", log.Lazy(func() slog.Value { + diff := set.From(active).Difference(set.From(orig)) + return slog.AnyValue(diff.Slice()) + }), + "Removed", log.Lazy(func() slog.Value { + diff := set.From(orig).Difference(set.From(active)) + return slog.AnyValue(diff.Slice()) + }), + "Unchanged", log.Lazy(func() slog.Value { + both := set.From(active).Intersect(set.From(orig)) + return slog.AnyValue(both.Slice()) + }), + ) + + return active +} + // evmCallArgs mirrors the parameters of the [EVM] methods Call(), CallCode(), // DelegateCall() and StaticCall(). Its fields are identical to those of the // parameters, prepended with the receiver name and call type. As diff --git a/go.mod b/go.mod index b545a4e8765..fc6cff6c41b 100644 --- a/go.mod +++ b/go.mod @@ -32,12 +32,13 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/google/gofuzz v1.2.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 + github.com/hashicorp/go-set/v3 v3.0.0 github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.2.4 diff --git a/go.sum b/go.sum index bb4ded5c2ff..7ae4c9bff70 100644 --- a/go.sum +++ b/go.sum @@ -294,8 +294,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -334,6 +334,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-set/v3 v3.0.0 h1:CaJBQvQCOWoftrBcDt7Nwgo0kdpmrKxar/x2o6pV9JA= +github.com/hashicorp/go-set/v3 v3.0.0/go.mod h1:IEghM2MpE5IaNvL+D7X480dfNtxjRXZ6VMpK3C8s2ok= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -547,6 +549,7 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shoenig/test v1.11.0 h1:NoPa5GIoBwuqzIviCrnUJa+t5Xb4xi5Z+zODJnIDsEQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= From 3996f8a1684112a39a04f28964a4002f2418a3ee Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Fri, 21 Feb 2025 11:38:15 +0000 Subject: [PATCH 07/15] refactor: precise error message in `Lazy` test --- log/value.libevm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/value.libevm_test.go b/log/value.libevm_test.go index de1005d0d48..c8f15d46b30 100644 --- a/log/value.libevm_test.go +++ b/log/value.libevm_test.go @@ -62,6 +62,6 @@ func TestLazy(t *testing.T) { log.Info("", key, fn) log.Debug("", "not evaluated", fn) - assert.Contains(t, out.String(), wantLogged, "evaluation of lazy function is logged") + assert.Containsf(t, out.String(), wantLogged, "evaluation of %T function is logged", fn) assert.Equalf(t, 1, gotNumEvaluations, "number of evaluations of %T function", fn) } From 4c699c8c179447b55f69a43d1af4a873d31a61f5 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Fri, 21 Feb 2025 15:08:21 +0000 Subject: [PATCH 08/15] feat: minimal `set` package --- core/vm/contracts.libevm.go | 8 ++--- go.mod | 3 +- go.sum | 7 ++--- libevm/set/set.go | 59 +++++++++++++++++++++++++++++++++++++ libevm/set/set_test.go | 56 +++++++++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 11 deletions(-) create mode 100644 libevm/set/set.go create mode 100644 libevm/set/set_test.go diff --git a/core/vm/contracts.libevm.go b/core/vm/contracts.libevm.go index 60cf9962921..785647407b9 100644 --- a/core/vm/contracts.libevm.go +++ b/core/vm/contracts.libevm.go @@ -20,13 +20,13 @@ import ( "fmt" "math/big" - set "github.com/hashicorp/go-set/v3" "github.com/holiman/uint256" "golang.org/x/exp/slog" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/libevm/set" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/params" ) @@ -41,15 +41,15 @@ func ActivePrecompiles(rules params.Rules) []common.Address { log.Debug( "Overriding active precompiles", "Added", log.Lazy(func() slog.Value { - diff := set.From(active).Difference(set.From(orig)) + diff := set.From(active...).Sub(set.From(orig...)) return slog.AnyValue(diff.Slice()) }), "Removed", log.Lazy(func() slog.Value { - diff := set.From(orig).Difference(set.From(active)) + diff := set.From(orig...).Sub(set.From(active...)) return slog.AnyValue(diff.Slice()) }), "Unchanged", log.Lazy(func() slog.Value { - both := set.From(active).Intersect(set.From(orig)) + both := set.From(active...).Intersect(set.From(orig...)) return slog.AnyValue(both.Slice()) }), ) diff --git a/go.mod b/go.mod index fc6cff6c41b..b545a4e8765 100644 --- a/go.mod +++ b/go.mod @@ -32,13 +32,12 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.5.9 github.com/google/gofuzz v1.2.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 - github.com/hashicorp/go-set/v3 v3.0.0 github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.2.4 diff --git a/go.sum b/go.sum index 7ae4c9bff70..bb4ded5c2ff 100644 --- a/go.sum +++ b/go.sum @@ -294,8 +294,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -334,8 +334,6 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-set/v3 v3.0.0 h1:CaJBQvQCOWoftrBcDt7Nwgo0kdpmrKxar/x2o6pV9JA= -github.com/hashicorp/go-set/v3 v3.0.0/go.mod h1:IEghM2MpE5IaNvL+D7X480dfNtxjRXZ6VMpK3C8s2ok= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -549,7 +547,6 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shoenig/test v1.11.0 h1:NoPa5GIoBwuqzIviCrnUJa+t5Xb4xi5Z+zODJnIDsEQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= diff --git a/libevm/set/set.go b/libevm/set/set.go new file mode 100644 index 00000000000..4f055434e19 --- /dev/null +++ b/libevm/set/set.go @@ -0,0 +1,59 @@ +// Copyright 2025 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +// Package set provides a generic implementation of a set. +package set + +// A Set is a generic set implementation. +type Set[T comparable] map[T]struct{} + +// From returns a Set containing the elements. +func From[T comparable](elements ...T) Set[T] { + s := make(Set[T], len(elements)) + for _, e := range elements { + s[e] = struct{}{} + } + return s +} + +// Sub returns the elements in `s` that aren't in `t`. +func (s Set[T]) Sub(t Set[T]) Set[T] { + return s.alsoIn(t, false) +} + +// Intersect returns the intersection of `s` and `t`. +func (s Set[T]) Intersect(t Set[T]) Set[T] { + return s.alsoIn(t, true) +} + +func (s Set[T]) alsoIn(t Set[T], inBoth bool) Set[T] { + res := make(Set[T]) + for el := range s { + if _, ok := t[el]; ok == inBoth { + res[el] = struct{}{} + } + } + return res +} + +// Slice returns the elements of `s` as a slice. +func (s Set[T]) Slice() []T { + sl := make([]T, 0, len(s)) + for el := range s { + sl = append(sl, el) + } + return sl +} diff --git a/libevm/set/set_test.go b/libevm/set/set_test.go new file mode 100644 index 00000000000..888dbbe3d0e --- /dev/null +++ b/libevm/set/set_test.go @@ -0,0 +1,56 @@ +// Copyright 2025 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +package set + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSub(t *testing.T) { + for _, tt := range [][3][]int{ // start, sub, want + {{}, {}, {}}, + {{0}, {}, {0}}, + {{}, {0}, {}}, + {{0, 1}, {0}, {1}}, + {{0, 1}, {1}, {0}}, + } { + in, sub := tt[0], tt[1] + want := tt[2] + got := From(in...).Sub(From(sub...)).Slice() + assert.Equalf(t, want, got, "Set(%v).Sub(%v)", in, sub) + } +} + +func TestIntersect(t *testing.T) { + for _, tt := range [][3][]int{ // L, R, intersection + {{}, {}, {}}, + {{0}, {}, {}}, + {{0}, {0}, {0}}, + {{0, 1}, {0}, {0}}, + {{0, 1}, {1}, {1}}, + } { + want := tt[2] + + for i := 0; i <= 1; i++ { // commutativity + lhs, rhs := tt[i], tt[1-i] + got := From(lhs...).Intersect(From(rhs...)).Slice() + assert.Equalf(t, want, got, "Set(%v).Intersect(%v)", lhs, rhs) + } + } +} From d3101d1dae7e3039c189f585c2cbe6df5beafda1 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Fri, 21 Feb 2025 16:03:48 +0000 Subject: [PATCH 09/15] chore: log `RulesAllowlistHooks` blocking only --- core/state_transition.libevm.go | 14 +++++++++++++- core/vm/evm.go | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/state_transition.libevm.go b/core/state_transition.libevm.go index 49e509d5637..56f1b72b7cb 100644 --- a/core/state_transition.libevm.go +++ b/core/state_transition.libevm.go @@ -16,10 +16,22 @@ package core +import ( + "github.com/ava-labs/libevm/log" +) + // canExecuteTransaction is a convenience wrapper for calling the // [params.RulesHooks.CanExecuteTransaction] hook. func (st *StateTransition) canExecuteTransaction() error { bCtx := st.evm.Context rules := st.evm.ChainConfig().Rules(bCtx.BlockNumber, bCtx.Random != nil, bCtx.Time) - return rules.Hooks().CanExecuteTransaction(st.msg.From, st.msg.To, st.state) + if err := rules.Hooks().CanExecuteTransaction(st.msg.From, st.msg.To, st.state); err != nil { + log.Debug( + "Transaction execution blocked by libevm hook", + "Hooks", log.TypeOf(rules.Hooks()), + "Reason", err, + ) + return err + } + return nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index 200de88f132..88501c4aac8 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -464,6 +464,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, addrs := &libevm.AddressContext{Origin: evm.Origin, Caller: caller.Address(), Self: address} gas, err := evm.chainRules.Hooks().CanCreateContract(addrs, gas, evm.StateDB) if err != nil { + log.Debug( + "Contract creation blocked by libevm hook", + "Hooks", log.TypeOf(evm.chainRules.Hooks()), + "Reason", err, + ) return nil, common.Address{}, gas, err } //libevm:end From 87e1ff7a257140e05a9d17d006dc7c54bc337618 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Mon, 24 Feb 2025 11:36:14 +0000 Subject: [PATCH 10/15] refactor(log): `TypeOf` returns a `Lazy` --- log/value.libevm.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/log/value.libevm.go b/log/value.libevm.go index 7f8034e79d7..da86e85d1af 100644 --- a/log/value.libevm.go +++ b/log/value.libevm.go @@ -22,18 +22,6 @@ import ( "golang.org/x/exp/slog" ) -// TypeOf returns a LogValuer that reports the concrete type of `v` as -// determined with the `%T` [fmt] verb. -func TypeOf(v any) slog.LogValuer { - return concreteTypeValue{v} -} - -type concreteTypeValue struct{ v any } - -func (v concreteTypeValue) LogValue() slog.Value { - return slog.StringValue(fmt.Sprintf("%T", v.v)) -} - // A Lazy function defers its execution until and if logging is performed. type Lazy func() slog.Value @@ -43,3 +31,11 @@ var _ slog.LogValuer = Lazy(nil) func (l Lazy) LogValue() slog.Value { return l() } + +// TypeOf returns a Lazy function that reports the concrete type of `v` as +// determined with the `%T` [fmt] verb. +func TypeOf(v any) Lazy { + return Lazy(func() slog.Value { + return slog.StringValue(fmt.Sprintf("%T", v)) + }) +} From 888e35095397bc4fa76fb92337706511267abdd1 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Mon, 24 Feb 2025 12:10:41 +0000 Subject: [PATCH 11/15] chore: log from+to when tx blocked --- core/state_transition.libevm.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/state_transition.libevm.go b/core/state_transition.libevm.go index 56f1b72b7cb..26920a0d728 100644 --- a/core/state_transition.libevm.go +++ b/core/state_transition.libevm.go @@ -28,6 +28,8 @@ func (st *StateTransition) canExecuteTransaction() error { if err := rules.Hooks().CanExecuteTransaction(st.msg.From, st.msg.To, st.state); err != nil { log.Debug( "Transaction execution blocked by libevm hook", + "From", st.msg.From, + "To", st.msg.To, "Hooks", log.TypeOf(rules.Hooks()), "Reason", err, ) From c0cd552e5913a6fbadc8e1344f23f7889f8eba55 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Mon, 24 Feb 2025 12:56:33 +0000 Subject: [PATCH 12/15] refactor: abstract `vm.EVM.canCreateContract()` hook wrapper --- core/vm/evm.go | 9 +-------- core/vm/evm.libevm.go | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 core/vm/evm.libevm.go diff --git a/core/vm/evm.go b/core/vm/evm.go index 88501c4aac8..b7bde87f98e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -23,7 +23,6 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" - "github.com/ava-labs/libevm/libevm" "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/params" "github.com/holiman/uint256" @@ -461,14 +460,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // This check MUST be placed after the caller's nonce is incremented but // before all other state-modifying behaviour, even if changes may be // reverted to the snapshot. - addrs := &libevm.AddressContext{Origin: evm.Origin, Caller: caller.Address(), Self: address} - gas, err := evm.chainRules.Hooks().CanCreateContract(addrs, gas, evm.StateDB) + gas, err := evm.canCreateContract(caller, address, gas) if err != nil { - log.Debug( - "Contract creation blocked by libevm hook", - "Hooks", log.TypeOf(evm.chainRules.Hooks()), - "Reason", err, - ) return nil, common.Address{}, gas, err } //libevm:end diff --git a/core/vm/evm.libevm.go b/core/vm/evm.libevm.go new file mode 100644 index 00000000000..080086df618 --- /dev/null +++ b/core/vm/evm.libevm.go @@ -0,0 +1,45 @@ +// Copyright 2025 the libevm authors. +// +// The libevm additions to go-ethereum are free software: you can redistribute +// them and/or modify them 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 libevm additions are distributed in the hope that they 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 +// . + +package vm + +import ( + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/libevm" + "github.com/ava-labs/libevm/log" +) + +// canCreateContract is a convenience wrapper for calling the +// [params.RulesHooks.CanCreateContract] hook. +func (evm *EVM) canCreateContract(caller ContractRef, contractToCreate common.Address, gas uint64) (remainingGas uint64, _ error) { + addrs := &libevm.AddressContext{Origin: evm.Origin, Caller: caller.Address(), Self: contractToCreate} + gas, err := evm.chainRules.Hooks().CanCreateContract(addrs, gas, evm.StateDB) + + // NOTE that this block only performs logging and that all paths propagate + // `(gas, err)` unmodified. + if err != nil { + log.Debug( + "Contract creation blocked by libevm hook", + "Origin", addrs.Origin, + "Caller", addrs.Caller, + "Contract", addrs.Self, + "Hooks", log.TypeOf(evm.chainRules.Hooks()), + "Reason", err, + ) + } + + return gas, err +} From a1aad6f4badd1821cf9c00fde131800b4333b9d3 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Mon, 24 Feb 2025 13:00:22 +0000 Subject: [PATCH 13/15] doc(log): correct `Lazy` comment --- log/value.libevm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/value.libevm.go b/log/value.libevm.go index da86e85d1af..8c4ab0aaf36 100644 --- a/log/value.libevm.go +++ b/log/value.libevm.go @@ -22,7 +22,7 @@ import ( "golang.org/x/exp/slog" ) -// A Lazy function defers its execution until and if logging is performed. +// A Lazy function defers its execution until logging is performed. type Lazy func() slog.Value var _ slog.LogValuer = Lazy(nil) From 570fb7aec1050fd45aa6e4a520dbf1a2446615d5 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Mon, 24 Feb 2025 13:09:25 +0000 Subject: [PATCH 14/15] chore(ci): mark `eth/tracers/internal/tracetest` flaky --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 701fe6b4cd2..47e20c549fd 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -22,7 +22,7 @@ jobs: go_test_short: env: - FLAKY_REGEX: "ava-labs/libevm/(triedb/pathdb|eth|eth/tracers/js|eth/tracers/logger|accounts/abi/bind|accounts/keystore|eth/downloader|miner|ethclient|ethclient/gethclient|eth/catalyst)$" + FLAKY_REGEX: "ava-labs/libevm/(triedb/pathdb|eth|eth/tracers/js|eth/tracers/logger|eth/tracers/internal/tracetest|accounts/abi/bind|accounts/keystore|eth/downloader|miner|ethclient|ethclient/gethclient|eth/catalyst)$" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From bf67df46f08d6d10886163e4ed72a18ec0a47582 Mon Sep 17 00:00:00 2001 From: Arran Schlosberg Date: Mon, 24 Feb 2025 16:46:10 +0000 Subject: [PATCH 15/15] chore: use lowercase log-attribute keys --- core/state_transition.libevm.go | 8 ++++---- core/vm/contracts.libevm.go | 6 +++--- core/vm/evm.go | 2 +- core/vm/evm.libevm.go | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/state_transition.libevm.go b/core/state_transition.libevm.go index 26920a0d728..701cc4d5ee7 100644 --- a/core/state_transition.libevm.go +++ b/core/state_transition.libevm.go @@ -28,10 +28,10 @@ func (st *StateTransition) canExecuteTransaction() error { if err := rules.Hooks().CanExecuteTransaction(st.msg.From, st.msg.To, st.state); err != nil { log.Debug( "Transaction execution blocked by libevm hook", - "From", st.msg.From, - "To", st.msg.To, - "Hooks", log.TypeOf(rules.Hooks()), - "Reason", err, + "from", st.msg.From, + "to", st.msg.To, + "hooks", log.TypeOf(rules.Hooks()), + "reason", err, ) return err } diff --git a/core/vm/contracts.libevm.go b/core/vm/contracts.libevm.go index 785647407b9..12271874602 100644 --- a/core/vm/contracts.libevm.go +++ b/core/vm/contracts.libevm.go @@ -40,15 +40,15 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // some duplication in favour of simplified code. log.Debug( "Overriding active precompiles", - "Added", log.Lazy(func() slog.Value { + "added", log.Lazy(func() slog.Value { diff := set.From(active...).Sub(set.From(orig...)) return slog.AnyValue(diff.Slice()) }), - "Removed", log.Lazy(func() slog.Value { + "removed", log.Lazy(func() slog.Value { diff := set.From(orig...).Sub(set.From(active...)) return slog.AnyValue(diff.Slice()) }), - "Unchanged", log.Lazy(func() slog.Value { + "unchanged", log.Lazy(func() slog.Value { both := set.From(active...).Intersect(set.From(orig...)) return slog.AnyValue(both.Slice()) }), diff --git a/core/vm/evm.go b/core/vm/evm.go index b7bde87f98e..ca3f5d21037 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -40,7 +40,7 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { if p, override := evm.chainRules.Hooks().PrecompileOverride(addr); override { - log.Debug("Overriding precompile", "Address", addr, "Implementation", log.TypeOf(p)) + log.Debug("Overriding precompile", "address", addr, "implementation", log.TypeOf(p)) return p, p != nil } var precompiles map[common.Address]PrecompiledContract diff --git a/core/vm/evm.libevm.go b/core/vm/evm.libevm.go index 080086df618..48278c28c37 100644 --- a/core/vm/evm.libevm.go +++ b/core/vm/evm.libevm.go @@ -33,11 +33,11 @@ func (evm *EVM) canCreateContract(caller ContractRef, contractToCreate common.Ad if err != nil { log.Debug( "Contract creation blocked by libevm hook", - "Origin", addrs.Origin, - "Caller", addrs.Caller, - "Contract", addrs.Self, - "Hooks", log.TypeOf(evm.chainRules.Hooks()), - "Reason", err, + "origin", addrs.Origin, + "caller", addrs.Caller, + "contract", addrs.Self, + "hooks", log.TypeOf(evm.chainRules.Hooks()), + "reason", err, ) }