diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go
index e000c563e41..71dc0bb14f8 100644
--- a/core/state/snapshot/snapshot.go
+++ b/core/state/snapshot/snapshot.go
@@ -832,6 +832,8 @@ func (t *Tree) disklayer() *diskLayer {
case *diskLayer:
return layer
case *diffLayer:
+ layer.lock.RLock()
+ defer layer.lock.RUnlock()
return layer.origin
default:
panic(fmt.Sprintf("%T: undefined layer", snap))
@@ -863,7 +865,7 @@ func (t *Tree) generating() (bool, error) {
return layer.genMarker != nil, nil
}
-// DiskRoot is a external helper function to return the disk layer root.
+// DiskRoot is an external helper function to return the disk layer root.
func (t *Tree) DiskRoot() common.Hash {
t.lock.Lock()
defer t.lock.Unlock()
diff --git a/core/state/statedb.go b/core/state/statedb.go
index ab2c3e55416..f9ee07bf270 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -19,6 +19,7 @@ package state
import (
"fmt"
+ "maps"
"sort"
"time"
@@ -693,18 +694,18 @@ func (s *StateDB) Copy() *StateDB {
db: s.db,
trie: s.db.CopyTrie(s.trie),
originalRoot: s.originalRoot,
- accounts: make(map[common.Hash][]byte),
- storages: make(map[common.Hash]map[common.Hash][]byte),
- accountsOrigin: make(map[common.Address][]byte),
- storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
+ accounts: copySet(s.accounts),
+ storages: copy2DSet(s.storages),
+ accountsOrigin: copySet(s.accountsOrigin),
+ storagesOrigin: copy2DSet(s.storagesOrigin),
stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
- stateObjectsDestruct: make(map[common.Address]*types.StateAccount, len(s.stateObjectsDestruct)),
+ stateObjectsDestruct: maps.Clone(s.stateObjectsDestruct),
refund: s.refund,
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
logSize: s.logSize,
- preimages: make(map[common.Hash][]byte, len(s.preimages)),
+ preimages: maps.Clone(s.preimages),
journal: newJournal(),
hasher: crypto.NewKeccakState(),
@@ -747,16 +748,6 @@ func (s *StateDB) Copy() *StateDB {
}
state.stateObjectsDirty[addr] = struct{}{}
}
- // Deep copy the destruction markers.
- for addr, value := range s.stateObjectsDestruct {
- state.stateObjectsDestruct[addr] = value
- }
- // Deep copy the state changes made in the scope of block
- // along with their original values.
- state.accounts = copySet(s.accounts)
- state.storages = copy2DSet(s.storages)
- state.accountsOrigin = copySet(state.accountsOrigin)
- state.storagesOrigin = copy2DSet(state.storagesOrigin)
// Deep copy the logs occurred in the scope of block
for hash, logs := range s.logs {
@@ -767,10 +758,6 @@ func (s *StateDB) Copy() *StateDB {
}
state.logs[hash] = cpy
}
- // Deep copy the preimages occurred in the scope of block
- for hash, preimage := range s.preimages {
- state.preimages[hash] = preimage
- }
// Do we need to copy the access list and transient storage?
// In practice: No. At the start of a transaction, these two lists are empty.
// In practice, we only ever copy state _between_ transactions/blocks, never
diff --git a/core/types/rlp_payload.libevm.go b/core/types/rlp_payload.libevm.go
index ff3530d1692..b2478b9e36f 100644
--- a/core/types/rlp_payload.libevm.go
+++ b/core/types/rlp_payload.libevm.go
@@ -20,6 +20,7 @@ import (
"fmt"
"io"
+ "github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/libevm/pseudo"
"github.com/ava-labs/libevm/libevm/register"
"github.com/ava-labs/libevm/libevm/testonly"
@@ -365,3 +366,8 @@ func (e *StateAccountExtra) Format(s fmt.State, verb rune) {
}
_, _ = s.Write([]byte(out))
}
+
+// RLPHash returns the hash of the RLP encoding of `x`.
+func RLPHash(x any) common.Hash {
+ return rlpHash(x)
+}
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 79b4d653f12..269cdfd68ca 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -169,6 +169,9 @@ func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
if x == nil {
return nil, errInvalidPubkey
}
+ if !S256().IsOnCurve(x, y) {
+ return nil, errInvalidPubkey
+ }
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}
diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go
index 0e2ca7a1aa2..39c3aafdc90 100644
--- a/eth/gasprice/feehistory.go
+++ b/eth/gasprice/feehistory.go
@@ -42,6 +42,7 @@ const (
// maxBlockFetchers is the max number of goroutines to spin up to pull blocks
// for the fee history calculation (mostly relevant for LES).
maxBlockFetchers = 4
+ maxQueryLimit = 100
)
// blockFees represents a single block for processing
@@ -219,6 +220,9 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL
if len(rewardPercentiles) != 0 {
maxFeeHistory = oracle.maxBlockHistory
}
+ if len(rewardPercentiles) > maxQueryLimit {
+ return common.Big0, nil, nil, nil, fmt.Errorf("%w: over the query limit %d", errInvalidPercentile, maxQueryLimit)
+ }
if blocks > maxFeeHistory {
log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
blocks = maxFeeHistory
diff --git a/internal/build/download.go b/internal/build/download.go
index fda573df833..206c51dce1e 100644
--- a/internal/build/download.go
+++ b/internal/build/download.go
@@ -84,10 +84,12 @@ func (db *ChecksumDB) DownloadFile(url, dstPath string) error {
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("download error: %v", err)
- } else if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("download error: status %d", resp.StatusCode)
}
defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("download error: status %d", resp.StatusCode)
+ }
if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
return err
}
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 7da95b2e5d7..62eab926334 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -1992,7 +1992,7 @@ func (api *DebugAPI) GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNu
hash = h
} else {
block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
- if err != nil {
+ if block == nil || err != nil {
return nil, err
}
hash = block.Hash()
@@ -2011,7 +2011,7 @@ func (api *DebugAPI) GetRawBlock(ctx context.Context, blockNrOrHash rpc.BlockNum
hash = h
} else {
block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
- if err != nil {
+ if block == nil || err != nil {
return nil, err
}
hash = block.Hash()
@@ -2030,7 +2030,7 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block
hash = h
} else {
block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
- if err != nil {
+ if block == nil || err != nil {
return nil, err
}
hash = block.Hash()
diff --git a/params/config.go b/params/config.go
index 1708bc78de7..4690569f612 100644
--- a/params/config.go
+++ b/params/config.go
@@ -880,7 +880,7 @@ func newTimestampCompatError(what string, storedtime, newtime *uint64) *ConfigCo
NewTime: newtime,
RewindToTime: 0,
}
- if rew != nil {
+ if rew != nil && *rew != 0 {
err.RewindToTime = *rew - 1
}
return err
@@ -890,7 +890,15 @@ func (err *ConfigCompatError) Error() string {
if err.StoredBlock != nil {
return fmt.Sprintf("mismatching %s in database (have block %d, want block %d, rewindto block %d)", err.What, err.StoredBlock, err.NewBlock, err.RewindToBlock)
}
- return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp %d, rewindto timestamp %d)", err.What, err.StoredTime, err.NewTime, err.RewindToTime)
+
+ if err.StoredTime == nil && err.NewTime == nil {
+ return ""
+ } else if err.StoredTime == nil && err.NewTime != nil {
+ return fmt.Sprintf("mismatching %s in database (have timestamp nil, want timestamp %d, rewindto timestamp %d)", err.What, *err.NewTime, err.RewindToTime)
+ } else if err.StoredTime != nil && err.NewTime == nil {
+ return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp nil, rewindto timestamp %d)", err.What, *err.StoredTime, err.RewindToTime)
+ }
+ return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp %d, rewindto timestamp %d)", err.What, *err.StoredTime, *err.NewTime, err.RewindToTime)
}
// Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions
diff --git a/params/config_test.go b/params/config_test.go
index 707566aa46f..6b222d3a673 100644
--- a/params/config_test.go
+++ b/params/config_test.go
@@ -23,6 +23,7 @@ import (
"time"
"github.com/ava-labs/libevm/common/math"
+ "github.com/stretchr/testify/require"
)
func TestCheckCompatible(t *testing.T) {
@@ -137,3 +138,20 @@ func TestConfigRules(t *testing.T) {
t.Errorf("expected %v to be shanghai", stamp)
}
}
+
+func TestTimestampCompatError(t *testing.T) {
+ require.Equal(t, new(ConfigCompatError).Error(), "")
+
+ errWhat := "Shanghai fork timestamp"
+ require.Equal(t, newTimestampCompatError(errWhat, nil, newUint64(1681338455)).Error(),
+ "mismatching Shanghai fork timestamp in database (have timestamp nil, want timestamp 1681338455, rewindto timestamp 1681338454)")
+
+ require.Equal(t, newTimestampCompatError(errWhat, newUint64(1681338455), nil).Error(),
+ "mismatching Shanghai fork timestamp in database (have timestamp 1681338455, want timestamp nil, rewindto timestamp 1681338454)")
+
+ require.Equal(t, newTimestampCompatError(errWhat, newUint64(1681338455), newUint64(600624000)).Error(),
+ "mismatching Shanghai fork timestamp in database (have timestamp 1681338455, want timestamp 600624000, rewindto timestamp 600623999)")
+
+ require.Equal(t, newTimestampCompatError(errWhat, newUint64(0), newUint64(1681338455)).Error(),
+ "mismatching Shanghai fork timestamp in database (have timestamp 0, want timestamp 1681338455, rewindto timestamp 0)")
+}
diff --git a/params/version.libevm.go b/params/version.libevm.go
index a2aeacb01df..f477e579e83 100644
--- a/params/version.libevm.go
+++ b/params/version.libevm.go
@@ -21,8 +21,8 @@ const (
LibEVMVersionMinor = 2
LibEVMVersionPatch = 0
- LibEVMReleaseType ReleaseType = BetaRelease
- libEVMReleaseCandidate uint = 3 // ignored unless [LibEVMReleaseType] == [ReleaseCandidate]
+ LibEVMReleaseType ReleaseType = ReleaseCandidate
+ libEVMReleaseCandidate uint = 4 // ignored unless [LibEVMReleaseType] == [ReleaseCandidate]
)
// LibEVMVersion holds the textual version string of `libevm` modifications.
@@ -48,7 +48,7 @@ const (
// triplet.
//
// [semver v2]: https://semver.org/
-const LibEVMVersion = "1.13.14-0.2.0.beta"
+const LibEVMVersion = "1.13.14-0.2.0.rc.4"
// A ReleaseType is a suffix for [LibEVMVersion].
type ReleaseType string
diff --git a/rpc/http.go b/rpc/http.go
index dd376b1ecd5..f4b99429ef4 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -236,7 +236,7 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos
if _, err := buf.ReadFrom(resp.Body); err == nil {
body = buf.Bytes()
}
-
+ resp.Body.Close()
return nil, HTTPError{
Status: resp.Status,
StatusCode: resp.StatusCode,
diff --git a/trie/proof.go b/trie/proof.go
index a7874afd35b..e8ba8be7eb6 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -487,6 +487,10 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
}
// Ensure the received batch is monotonic increasing and contains no deletions
for i := 0; i < len(keys)-1; i++ {
+ // See: https://github.com/ava-labs/coreth/issues/907
+ if len(keys[i]) != len(keys[i+1]) {
+ return false, errKeysHaveDifferentLengths
+ }
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
return false, errors.New("range is not monotonically increasing")
}
diff --git a/trie/proof.libevm.go b/trie/proof.libevm.go
new file mode 100644
index 00000000000..b7b63ac66a1
--- /dev/null
+++ b/trie/proof.libevm.go
@@ -0,0 +1,23 @@
+// 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 trie
+
+import (
+ "errors"
+)
+
+var errKeysHaveDifferentLengths = errors.New("keys have different lengths")
diff --git a/trie/proof.libevm_test.go b/trie/proof.libevm_test.go
new file mode 100644
index 00000000000..17b2ddc398d
--- /dev/null
+++ b/trie/proof.libevm_test.go
@@ -0,0 +1,48 @@
+// 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 trie
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/ava-labs/libevm/common"
+)
+
+func TestRangeProofKeysWithDifferentLengths(t *testing.T) {
+ var (
+ root = common.HexToHash("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+ start = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
+ keys = [][]byte{
+ common.Hex2Bytes("1000000000000000000000000000000"),
+ common.Hex2Bytes("1000000000000000000000000000000000000000000000000000000000000000"),
+ }
+ values = [][]byte{
+ common.Hex2Bytes("02"),
+ common.Hex2Bytes("03"),
+ }
+ )
+ _, err := VerifyRangeProof(
+ root,
+ start,
+ keys,
+ values,
+ nil, // force it to use stacktrie
+ )
+ require.ErrorIs(t, err, errKeysHaveDifferentLengths)
+}