Skip to content

Commit 2a5e630

Browse files
committed
refactor: temporary extras require proof of lock
1 parent 910e897 commit 2a5e630

File tree

5 files changed

+138
-79
lines changed

5 files changed

+138
-79
lines changed

libevm/extraslock.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2025 the libevm authors.
2+
//
3+
// The libevm additions to go-ethereum are free software: you can redistribute
4+
// them and/or modify them under the terms of the GNU Lesser General Public License
5+
// as published by the Free Software Foundation, either version 3 of the License,
6+
// or (at your option) any later version.
7+
//
8+
// The libevm additions are distributed in the hope that they will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11+
// General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Lesser General Public License
14+
// along with the go-ethereum library. If not, see
15+
// <http://www.gnu.org/licenses/>.
16+
17+
package libevm
18+
19+
import (
20+
"errors"
21+
"sync"
22+
"sync/atomic"
23+
)
24+
25+
var (
26+
extrasMu sync.Mutex
27+
extrasHandle atomic.Uint64
28+
)
29+
30+
// WithTemporaryExtrasLock takes a global lock and calls `fn` with a handle that
31+
// can be used to prove that the lock is held. All package-specific temporary
32+
// overrides require this proof.
33+
//
34+
// WithTemporaryExtrasLock MUST NOT be used on a live chain. It is solely
35+
// intended for off-chain consumers that require access to extras.
36+
func WithTemporaryExtrasLock(fn func(lock ExtrasLock) error) error {
37+
extrasMu.Lock()
38+
defer func() {
39+
extrasHandle.Add(1)
40+
extrasMu.Unlock()
41+
}()
42+
43+
v := extrasHandle.Load()
44+
return fn(ExtrasLock{&v})
45+
}
46+
47+
// ErrExpiredExtrasLock is returned by [ExtrasLock.Verify] if the lock has been
48+
// persisted beyond the call to [WithTemporaryExtrasLock] that created it.
49+
var ErrExpiredExtrasLock = errors.New("libevm.ExtrasLock expired")
50+
51+
// An ExtrasLock is a handle that proves a current call to
52+
// [WithTemporaryExtrasLock].
53+
type ExtrasLock struct {
54+
handle *uint64
55+
}
56+
57+
// Verify verifies that the lock is valid.
58+
func (l ExtrasLock) Verify() error {
59+
if *l.handle != extrasHandle.Load() {
60+
return ErrExpiredExtrasLock
61+
}
62+
return nil
63+
}

libevm/extraslock_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2025 the libevm authors.
2+
//
3+
// The libevm additions to go-ethereum are free software: you can redistribute
4+
// them and/or modify them under the terms of the GNU Lesser General Public License
5+
// as published by the Free Software Foundation, either version 3 of the License,
6+
// or (at your option) any later version.
7+
//
8+
// The libevm additions are distributed in the hope that they will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11+
// General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Lesser General Public License
14+
// along with the go-ethereum library. If not, see
15+
// <http://www.gnu.org/licenses/>.
16+
17+
package libevm_test
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
24+
// Testing from outside the package to guarantee usage of the public API only.
25+
. "github.com/ava-labs/libevm/libevm"
26+
)
27+
28+
func TestExtrasLock(t *testing.T) {
29+
var zero ExtrasLock
30+
assert.Panics(t, func() { zero.Verify() }, "Verify() method of zero-value ExtrasLock{}")
31+
32+
assert.NoError(t,
33+
WithTemporaryExtrasLock((ExtrasLock).Verify),
34+
"WithTemporaryExtrasLock((ExtrasLock).Verify)",
35+
)
36+
37+
var persisted ExtrasLock
38+
WithTemporaryExtrasLock(func(l ExtrasLock) error {
39+
persisted = l
40+
return nil
41+
})
42+
43+
assert.ErrorIs(
44+
t, persisted.Verify(), ErrExpiredExtrasLock,
45+
"Verify() of persisted ExtrasLock",
46+
)
47+
}

libevm/temporary/temporary.go

Lines changed: 0 additions & 65 deletions
This file was deleted.

params/config.libevm.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"math/big"
2222
"reflect"
2323

24+
"github.com/ava-labs/libevm/libevm"
2425
"github.com/ava-labs/libevm/libevm/pseudo"
2526
"github.com/ava-labs/libevm/libevm/register"
2627
"github.com/ava-labs/libevm/log"
@@ -106,11 +107,18 @@ func payloadsAndConstructors[C ChainConfigHooks, R RulesHooks](e Extras[C, R]) (
106107
// call this function directly. Use the libevm/temporary.WithRegisteredExtras()
107108
// function instead as it atomically overrides all possible packages.
108109
func WithTempRegisteredExtras[C ChainConfigHooks, R RulesHooks](
110+
lock libevm.ExtrasLock,
109111
e Extras[C, R],
110-
fn func(ExtraPayloads[C, R]),
111-
) {
112+
fn func(ExtraPayloads[C, R]) error,
113+
) error {
114+
if err := lock.Verify(); err != nil {
115+
return err
116+
}
117+
112118
payloads, ctors := payloadsAndConstructors(e)
113-
registeredExtras.TempOverride(ctors, func() { fn(payloads) })
119+
var err error
120+
registeredExtras.TempOverride(ctors, func() { err = fn(payloads) })
121+
return err
114122
}
115123

116124
// TestOnlyClearRegisteredExtras clears the [Extras] previously passed to

params/config.libevm_test.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 the libevm authors.
1+
// Copyright 2024-2025 the libevm authors.
22
//
33
// The libevm additions to go-ethereum are free software: you can redistribute
44
// them and/or modify them under the terms of the GNU Lesser General Public License
@@ -13,6 +13,7 @@
1313
// You should have received a copy of the GNU Lesser General Public License
1414
// along with the go-ethereum library. If not, see
1515
// <http://www.gnu.org/licenses/>.
16+
1617
package params
1718

1819
import (
@@ -23,6 +24,7 @@ import (
2324
"github.com/stretchr/testify/assert"
2425
"github.com/stretchr/testify/require"
2526

27+
"github.com/ava-labs/libevm/libevm"
2628
"github.com/ava-labs/libevm/libevm/pseudo"
2729
"github.com/ava-labs/libevm/libevm/register"
2830
)
@@ -329,16 +331,20 @@ func TestTempRegisteredExtras(t *testing.T) {
329331

330332
t.Run("before_temp", testPrimaryExtras)
331333
t.Run("WithTempRegisteredExtras", func(t *testing.T) {
332-
WithTempRegisteredExtras(
333-
override,
334-
func(extras ExtraPayloads[overrideCC, overrideRules]) { // deliberately shadow `extras`
335-
assertRulesCopiedFromChainConfig(
336-
t, extras, "hello, world",
337-
func(cc *overrideCC, x string) { cc.X = x },
338-
func(r *overrideRules) string { return r.X },
339-
)
340-
},
341-
)
334+
err := libevm.WithTemporaryExtrasLock(func(lock libevm.ExtrasLock) error {
335+
return WithTempRegisteredExtras(
336+
lock, override,
337+
func(extras ExtraPayloads[overrideCC, overrideRules]) error { // deliberately shadow `extras`
338+
assertRulesCopiedFromChainConfig(
339+
t, extras, "hello, world",
340+
func(cc *overrideCC, x string) { cc.X = x },
341+
func(r *overrideRules) string { return r.X },
342+
)
343+
return nil
344+
},
345+
)
346+
})
347+
require.NoError(t, err)
342348
})
343349
t.Run("after_temp", testPrimaryExtras)
344350
}

0 commit comments

Comments
 (0)