Merge remote-tracking branch 'origin/master' into firehose-fh3.0

# Conflicts:
#	cmd/evm/internal/t8ntool/execution.go
#	cmd/evm/internal/t8ntool/transition.go
#	cmd/utils/flags.go
#	consensus/beacon/consensus.go
#	core/blockchain.go
#	core/state_processor.go
#	core/tracing/hooks.go
#	core/vm/runtime/runtime.go
#	eth/backend.go
#	eth/tracers/api.go
#	eth/tracers/dir.go
#	eth/tracers/directory/tracers.go
#	eth/tracers/internal/tracetest/calltrace_test.go
#	eth/tracers/internal/tracetest/flat_calltrace_test.go
#	eth/tracers/js/goja.go
#	eth/tracers/js/tracer_test.go
#	eth/tracers/live/noop.go
#	eth/tracers/logger/logger.go
#	eth/tracers/native/4byte.go
#	eth/tracers/native/call.go
#	eth/tracers/native/call_flat.go
#	eth/tracers/native/mux.go
#	eth/tracers/native/noop.go
#	eth/tracers/native/prestate.go
#	eth/tracers/tracers.go
#	go.mod
This commit is contained in:
Matthieu Vachon 2024-03-25 14:45:08 -04:00
commit 262c3117f6
167 changed files with 6249 additions and 1034 deletions

View file

@ -16,7 +16,7 @@ jobs:
os: linux os: linux
arch: amd64 arch: amd64
dist: bionic dist: bionic
go: 1.21.x go: 1.22.x
env: env:
- docker - docker
services: services:
@ -33,7 +33,7 @@ jobs:
os: linux os: linux
arch: arm64 arch: arm64
dist: bionic dist: bionic
go: 1.21.x go: 1.22.x
env: env:
- docker - docker
services: services:
@ -51,7 +51,7 @@ jobs:
os: linux os: linux
dist: bionic dist: bionic
sudo: required sudo: required
go: 1.21.x go: 1.22.x
env: env:
- azure-linux - azure-linux
git: git:
@ -85,7 +85,7 @@ jobs:
if: type = push if: type = push
os: osx os: osx
osx_image: xcode14.2 osx_image: xcode14.2
go: 1.21.x go: 1.22.x
env: env:
- azure-osx - azure-osx
git: git:
@ -101,7 +101,7 @@ jobs:
os: linux os: linux
arch: amd64 arch: amd64
dist: bionic dist: bionic
go: 1.21.x go: 1.22.x
script: script:
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES - travis_wait 30 go run build/ci.go test $TEST_PACKAGES
@ -110,14 +110,14 @@ jobs:
os: linux os: linux
arch: arm64 arch: arm64
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
script: script:
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES - travis_wait 30 go run build/ci.go test $TEST_PACKAGES
- stage: build - stage: build
os: linux os: linux
dist: bionic dist: bionic
go: 1.20.x go: 1.21.x
script: script:
- travis_wait 30 go run build/ci.go test $TEST_PACKAGES - travis_wait 30 go run build/ci.go test $TEST_PACKAGES
@ -126,7 +126,7 @@ jobs:
if: type = cron || (type = push && tag ~= /^v[0-9]/) if: type = cron || (type = push && tag ~= /^v[0-9]/)
os: linux os: linux
dist: bionic dist: bionic
go: 1.21.x go: 1.22.x
env: env:
- ubuntu-ppa - ubuntu-ppa
git: git:
@ -149,7 +149,7 @@ jobs:
if: type = cron if: type = cron
os: linux os: linux
dist: bionic dist: bionic
go: 1.21.x go: 1.22.x
env: env:
- azure-purge - azure-purge
git: git:
@ -162,7 +162,7 @@ jobs:
if: type = cron if: type = cron
os: linux os: linux
dist: bionic dist: bionic
go: 1.21.x go: 1.22.x
script: script:
- travis_wait 30 go run build/ci.go test -race $TEST_PACKAGES - travis_wait 30 go run build/ci.go test -race $TEST_PACKAGES

View file

@ -4,7 +4,7 @@ ARG VERSION=""
ARG BUILDNUM="" ARG BUILDNUM=""
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.21-alpine as builder FROM golang:1.22-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git RUN apk add --no-cache gcc musl-dev linux-headers git
@ -25,7 +25,7 @@ COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp EXPOSE 8545 8546 30303 30303/udp
ENTRYPOINT ["geth"] ENTRYPOINT ["geth"]
# Add some metadata labels to help programatic image consumption # Add some metadata labels to help programmatic image consumption
ARG COMMIT="" ARG COMMIT=""
ARG VERSION="" ARG VERSION=""
ARG BUILDNUM="" ARG BUILDNUM=""

View file

@ -4,7 +4,7 @@ ARG VERSION=""
ARG BUILDNUM="" ARG BUILDNUM=""
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.21-alpine as builder FROM golang:1.22-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git RUN apk add --no-cache gcc musl-dev linux-headers git
@ -24,7 +24,7 @@ COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp EXPOSE 8545 8546 30303 30303/udp
# Add some metadata labels to help programatic image consumption # Add some metadata labels to help programmatic image consumption
ARG COMMIT="" ARG COMMIT=""
ARG VERSION="" ARG VERSION=""
ARG BUILDNUM="" ARG BUILDNUM=""

View file

@ -2,7 +2,7 @@
# with Go source code. If you know what GOPATH is then you probably # with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make. # don't need to bother with make.
.PHONY: geth android ios evm all test clean .PHONY: geth all test lint clean devtools help
GOBIN = ./build/bin GOBIN = ./build/bin
GO ?= latest GO ?= latest
@ -47,4 +47,3 @@ devtools:
help: Makefile help: Makefile
@echo " Choose a command run in go-ethereum:" @echo " Choose a command run in go-ethereum:"
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /' @sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'
.PHONY: help

View file

@ -16,7 +16,7 @@ archives are published at https://geth.ethereum.org/downloads/.
For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth). For prerequisites and detailed build instructions please read the [Installation Instructions](https://geth.ethereum.org/docs/getting-started/installing-geth).
Building `geth` requires both a Go (version 1.19 or later) and a C compiler. You can install Building `geth` requires both a Go (version 1.21 or later) and a C compiler. You can install
them using your favourite package manager. Once the dependencies are installed, run them using your favourite package manager. Once the dependencies are installed, run
```shell ```shell

View file

@ -142,10 +142,10 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer // NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer
// from a single private key. // from a single private key.
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) { func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
if chainID == nil { if chainID == nil {
return nil, ErrNoChainID return nil, ErrNoChainID
} }
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
signer := types.LatestSignerForChainID(chainID) signer := types.LatestSignerForChainID(chainID)
return &TransactOpts{ return &TransactOpts{
From: keyAddr, From: keyAddr,

View file

@ -461,7 +461,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
sub, err := event.NewSubscription(func(quit <-chan struct{}) error { sub := event.NewSubscription(func(quit <-chan struct{}) error {
for _, log := range buff { for _, log := range buff {
select { select {
case logs <- log: case logs <- log:
@ -470,11 +470,8 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
} }
} }
return nil return nil
}), nil })
if err != nil {
return nil, nil, err
}
return logs, sub, nil return logs, sub, nil
} }

View file

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -31,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"golang.org/x/exp/slices"
) )
// Minimum amount of time between cache reloads. This limit applies if the platform does // Minimum amount of time between cache reloads. This limit applies if the platform does

View file

@ -23,6 +23,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"slices"
"testing" "testing"
"time" "time"
@ -30,7 +31,6 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"golang.org/x/exp/slices"
) )
var ( var (

View file

@ -500,7 +500,5 @@ func (ks *KeyStore) isUpdating() bool {
// zeroKey zeroes a private key in memory. // zeroKey zeroes a private key in memory.
func zeroKey(k *ecdsa.PrivateKey) { func zeroKey(k *ecdsa.PrivateKey) {
b := k.D.Bits() b := k.D.Bits()
for i := range b { clear(b)
b[i] = 0
}
} }

View file

@ -20,6 +20,7 @@ import (
"math/rand" "math/rand"
"os" "os"
"runtime" "runtime"
"slices"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -30,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"golang.org/x/exp/slices"
) )
var testSigData = make([]byte, 32) var testSigData = make([]byte, 32)

View file

@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
@ -72,11 +71,11 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
if err != nil { if err != nil {
return nil, fmt.Errorf("could not unmarshal public key from card: %v", err) return nil, fmt.Errorf("could not unmarshal public key from card: %v", err)
} }
secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes()) secret, _ := crypto.S256().ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes())
return &SecureChannelSession{ return &SecureChannelSession{
card: card, card: card,
secret: secret.Bytes(), secret: secret.Bytes(),
publicKey: elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y), publicKey: crypto.FromECDSAPub(&key.PublicKey),
}, nil }, nil
} }

View file

@ -17,35 +17,25 @@
package blsync package blsync
import ( import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/beacon/light/request" "github.com/ethereum/go-ethereum/beacon/light/request"
"github.com/ethereum/go-ethereum/beacon/light/sync" "github.com/ethereum/go-ethereum/beacon/light/sync"
"github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/common/lru"
ctypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"github.com/protolambda/zrnt/eth2/beacon/capella"
"github.com/protolambda/zrnt/eth2/configs"
"github.com/protolambda/ztyp/tree"
) )
// beaconBlockSync implements request.Module; it fetches the beacon blocks belonging // beaconBlockSync implements request.Module; it fetches the beacon blocks belonging
// to the validated and prefetch heads. // to the validated and prefetch heads.
type beaconBlockSync struct { type beaconBlockSync struct {
recentBlocks *lru.Cache[common.Hash, *capella.BeaconBlock] recentBlocks *lru.Cache[common.Hash, *types.BeaconBlock]
locked map[common.Hash]request.ServerAndID locked map[common.Hash]request.ServerAndID
serverHeads map[request.Server]common.Hash serverHeads map[request.Server]common.Hash
headTracker headTracker headTracker headTracker
lastHeadInfo types.HeadInfo lastHeadInfo types.HeadInfo
chainHeadFeed *event.Feed chainHeadFeed event.FeedOf[types.ChainHeadEvent]
} }
type headTracker interface { type headTracker interface {
@ -55,16 +45,19 @@ type headTracker interface {
} }
// newBeaconBlockSync returns a new beaconBlockSync. // newBeaconBlockSync returns a new beaconBlockSync.
func newBeaconBlockSync(headTracker headTracker, chainHeadFeed *event.Feed) *beaconBlockSync { func newBeaconBlockSync(headTracker headTracker) *beaconBlockSync {
return &beaconBlockSync{ return &beaconBlockSync{
headTracker: headTracker, headTracker: headTracker,
chainHeadFeed: chainHeadFeed, recentBlocks: lru.NewCache[common.Hash, *types.BeaconBlock](10),
recentBlocks: lru.NewCache[common.Hash, *capella.BeaconBlock](10), locked: make(map[common.Hash]request.ServerAndID),
locked: make(map[common.Hash]request.ServerAndID), serverHeads: make(map[request.Server]common.Hash),
serverHeads: make(map[request.Server]common.Hash),
} }
} }
func (s *beaconBlockSync) SubscribeChainHead(ch chan<- types.ChainHeadEvent) event.Subscription {
return s.chainHeadFeed.Subscribe(ch)
}
// Process implements request.Module. // Process implements request.Module.
func (s *beaconBlockSync) Process(requester request.Requester, events []request.Event) { func (s *beaconBlockSync) Process(requester request.Requester, events []request.Event) {
for _, event := range events { for _, event := range events {
@ -73,7 +66,7 @@ func (s *beaconBlockSync) Process(requester request.Requester, events []request.
sid, req, resp := event.RequestInfo() sid, req, resp := event.RequestInfo()
blockRoot := common.Hash(req.(sync.ReqBeaconBlock)) blockRoot := common.Hash(req.(sync.ReqBeaconBlock))
if resp != nil { if resp != nil {
s.recentBlocks.Add(blockRoot, resp.(*capella.BeaconBlock)) s.recentBlocks.Add(blockRoot, resp.(*types.BeaconBlock))
} }
if s.locked[blockRoot] == sid { if s.locked[blockRoot] == sid {
delete(s.locked, blockRoot) delete(s.locked, blockRoot)
@ -112,63 +105,11 @@ func (s *beaconBlockSync) tryRequestBlock(requester request.Requester, blockRoot
} }
} }
func blockHeadInfo(block *capella.BeaconBlock) types.HeadInfo { func blockHeadInfo(block *types.BeaconBlock) types.HeadInfo {
if block == nil { if block == nil {
return types.HeadInfo{} return types.HeadInfo{}
} }
return types.HeadInfo{Slot: uint64(block.Slot), BlockRoot: beaconBlockHash(block)} return types.HeadInfo{Slot: block.Slot(), BlockRoot: block.Root()}
}
// beaconBlockHash calculates the hash of a beacon block.
func beaconBlockHash(beaconBlock *capella.BeaconBlock) common.Hash {
return common.Hash(beaconBlock.HashTreeRoot(configs.Mainnet, tree.GetHashFn()))
}
// getExecBlock extracts the execution block from the beacon block's payload.
func getExecBlock(beaconBlock *capella.BeaconBlock) (*ctypes.Block, error) {
payload := &beaconBlock.Body.ExecutionPayload
txs := make([]*ctypes.Transaction, len(payload.Transactions))
for i, opaqueTx := range payload.Transactions {
var tx ctypes.Transaction
if err := tx.UnmarshalBinary(opaqueTx); err != nil {
return nil, fmt.Errorf("failed to parse tx %d: %v", i, err)
}
txs[i] = &tx
}
withdrawals := make([]*ctypes.Withdrawal, len(payload.Withdrawals))
for i, w := range payload.Withdrawals {
withdrawals[i] = &ctypes.Withdrawal{
Index: uint64(w.Index),
Validator: uint64(w.ValidatorIndex),
Address: common.Address(w.Address),
Amount: uint64(w.Amount),
}
}
wroot := ctypes.DeriveSha(ctypes.Withdrawals(withdrawals), trie.NewStackTrie(nil))
execHeader := &ctypes.Header{
ParentHash: common.Hash(payload.ParentHash),
UncleHash: ctypes.EmptyUncleHash,
Coinbase: common.Address(payload.FeeRecipient),
Root: common.Hash(payload.StateRoot),
TxHash: ctypes.DeriveSha(ctypes.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: common.Hash(payload.ReceiptsRoot),
Bloom: ctypes.Bloom(payload.LogsBloom),
Difficulty: common.Big0,
Number: new(big.Int).SetUint64(uint64(payload.BlockNumber)),
GasLimit: uint64(payload.GasLimit),
GasUsed: uint64(payload.GasUsed),
Time: uint64(payload.Timestamp),
Extra: []byte(payload.ExtraData),
MixDigest: common.Hash(payload.PrevRandao), // reused in merge
Nonce: ctypes.BlockNonce{}, // zero
BaseFee: (*uint256.Int)(&payload.BaseFeePerGas).ToBig(),
WithdrawalsHash: &wroot,
}
execBlock := ctypes.NewBlockWithHeader(execHeader).WithBody(txs, nil).WithWithdrawals(withdrawals)
if execBlockHash := execBlock.Hash(); execBlockHash != common.Hash(payload.BlockHash) {
return execBlock, fmt.Errorf("Sanity check failed, payload hash does not match (expected %x, got %x)", common.Hash(payload.BlockHash), execBlockHash)
}
return execBlock, nil
} }
func (s *beaconBlockSync) updateEventFeed() { func (s *beaconBlockSync) updateEventFeed() {
@ -190,14 +131,16 @@ func (s *beaconBlockSync) updateEventFeed() {
return return
} }
s.lastHeadInfo = headInfo s.lastHeadInfo = headInfo
// new head block and finality info available; extract executable data and send event to feed // new head block and finality info available; extract executable data and send event to feed
execBlock, err := getExecBlock(headBlock) execBlock, err := headBlock.ExecutionPayload()
if err != nil { if err != nil {
log.Error("Error extracting execution block from validated beacon block", "error", err) log.Error("Error extracting execution block from validated beacon block", "error", err)
return return
} }
s.chainHeadFeed.Send(types.ChainHeadEvent{ s.chainHeadFeed.Send(types.ChainHeadEvent{
HeadBlock: engine.BlockToExecutableData(execBlock, nil, nil).ExecutionPayload, BeaconHead: head.Header,
Finalized: common.Hash(finality.Finalized.PayloadHeader.BlockHash), Block: execBlock,
Finalized: finality.Finalized.PayloadHeader.BlockHash(),
}) })
} }

View file

@ -23,70 +23,69 @@ import (
"github.com/ethereum/go-ethereum/beacon/light/sync" "github.com/ethereum/go-ethereum/beacon/light/sync"
"github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event" zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/beacon/capella" "github.com/protolambda/zrnt/eth2/beacon/deneb"
"github.com/protolambda/zrnt/eth2/configs"
"github.com/protolambda/ztyp/tree"
) )
var ( var (
testServer1 = "testServer1" testServer1 = "testServer1"
testServer2 = "testServer2" testServer2 = "testServer2"
testBlock1 = &capella.BeaconBlock{ testBlock1 = types.NewBeaconBlock(&deneb.BeaconBlock{
Slot: 123, Slot: 123,
Body: capella.BeaconBlockBody{ Body: deneb.BeaconBlockBody{
ExecutionPayload: capella.ExecutionPayload{BlockNumber: 456}, ExecutionPayload: deneb.ExecutionPayload{
BlockNumber: 456,
BlockHash: zrntcommon.Hash32(common.HexToHash("905ac721c4058d9ed40b27b6b9c1bdd10d4333e4f3d9769100bf9dfb80e5d1f6")),
},
}, },
} })
testBlock2 = &capella.BeaconBlock{ testBlock2 = types.NewBeaconBlock(&deneb.BeaconBlock{
Slot: 124, Slot: 124,
Body: capella.BeaconBlockBody{ Body: deneb.BeaconBlockBody{
ExecutionPayload: capella.ExecutionPayload{BlockNumber: 457}, ExecutionPayload: deneb.ExecutionPayload{
BlockNumber: 457,
BlockHash: zrntcommon.Hash32(common.HexToHash("011703f39c664efc1c6cf5f49ca09b595581eec572d4dfddd3d6179a9e63e655")),
},
}, },
} })
) )
func init() {
eb1, _ := getExecBlock(testBlock1)
testBlock1.Body.ExecutionPayload.BlockHash = tree.Root(eb1.Hash())
eb2, _ := getExecBlock(testBlock2)
testBlock2.Body.ExecutionPayload.BlockHash = tree.Root(eb2.Hash())
}
func TestBlockSync(t *testing.T) { func TestBlockSync(t *testing.T) {
ht := &testHeadTracker{} ht := &testHeadTracker{}
eventFeed := new(event.Feed) blockSync := newBeaconBlockSync(ht)
blockSync := newBeaconBlockSync(ht, eventFeed)
headCh := make(chan types.ChainHeadEvent, 16) headCh := make(chan types.ChainHeadEvent, 16)
eventFeed.Subscribe(headCh) blockSync.SubscribeChainHead(headCh)
ts := sync.NewTestScheduler(t, blockSync) ts := sync.NewTestScheduler(t, blockSync)
ts.AddServer(testServer1, 1) ts.AddServer(testServer1, 1)
ts.AddServer(testServer2, 1) ts.AddServer(testServer2, 1)
expHeadBlock := func(tci int, expHead *capella.BeaconBlock) { expHeadBlock := func(expHead *types.BeaconBlock) {
t.Helper()
var expNumber, headNumber uint64 var expNumber, headNumber uint64
if expHead != nil { if expHead != nil {
expNumber = uint64(expHead.Body.ExecutionPayload.BlockNumber) p, _ := expHead.ExecutionPayload()
expNumber = p.NumberU64()
} }
select { select {
case event := <-headCh: case event := <-headCh:
headNumber = event.HeadBlock.Number headNumber = event.Block.NumberU64()
default: default:
} }
if headNumber != expNumber { if headNumber != expNumber {
t.Errorf("Wrong head block in test case #%d (expected block number %d, got %d)", tci, expNumber, headNumber) t.Errorf("Wrong head block, expected block number %d, got %d)", expNumber, headNumber)
} }
} }
// no block requests expected until head tracker knows about a head // no block requests expected until head tracker knows about a head
ts.Run(1) ts.Run(1)
expHeadBlock(1, nil) expHeadBlock(nil)
// set block 1 as prefetch head, announced by server 2 // set block 1 as prefetch head, announced by server 2
head1 := blockHeadInfo(testBlock1) head1 := blockHeadInfo(testBlock1)
ht.prefetch = head1 ht.prefetch = head1
ts.ServerEvent(sync.EvNewHead, testServer2, head1) ts.ServerEvent(sync.EvNewHead, testServer2, head1)
// expect request to server 2 which has announced the head // expect request to server 2 which has announced the head
ts.Run(2, testServer2, sync.ReqBeaconBlock(head1.BlockRoot)) ts.Run(2, testServer2, sync.ReqBeaconBlock(head1.BlockRoot))
@ -95,12 +94,12 @@ func TestBlockSync(t *testing.T) {
ts.AddAllowance(testServer2, 1) ts.AddAllowance(testServer2, 1)
ts.Run(3) ts.Run(3)
// head block still not expected as the fetched block is not the validated head yet // head block still not expected as the fetched block is not the validated head yet
expHeadBlock(3, nil) expHeadBlock(nil)
// set as validated head, expect no further requests but block 1 set as head block // set as validated head, expect no further requests but block 1 set as head block
ht.validated.Header = blockHeader(testBlock1) ht.validated.Header = testBlock1.Header()
ts.Run(4) ts.Run(4)
expHeadBlock(4, testBlock1) expHeadBlock(testBlock1)
// set block 2 as prefetch head, announced by server 1 // set block 2 as prefetch head, announced by server 1
head2 := blockHeadInfo(testBlock2) head2 := blockHeadInfo(testBlock2)
@ -114,26 +113,16 @@ func TestBlockSync(t *testing.T) {
ts.Run(6) ts.Run(6)
// set as validated head before retrieving block; now it's assumed to be available from server 2 too // set as validated head before retrieving block; now it's assumed to be available from server 2 too
ht.validated.Header = blockHeader(testBlock2) ht.validated.Header = testBlock2.Header()
// expect req2 retry to server 2 // expect req2 retry to server 2
ts.Run(7, testServer2, sync.ReqBeaconBlock(head2.BlockRoot)) ts.Run(7, testServer2, sync.ReqBeaconBlock(head2.BlockRoot))
// now head block should be unavailable again // now head block should be unavailable again
expHeadBlock(4, nil) expHeadBlock(nil)
// valid response, now head block should be block 2 immediately as it is already validated // valid response, now head block should be block 2 immediately as it is already validated
ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testBlock2) ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testBlock2)
ts.Run(8) ts.Run(8)
expHeadBlock(5, testBlock2) expHeadBlock(testBlock2)
}
func blockHeader(block *capella.BeaconBlock) types.Header {
return types.Header{
Slot: uint64(block.Slot),
ProposerIndex: uint64(block.ProposerIndex),
ParentRoot: common.Hash(block.ParentRoot),
StateRoot: common.Hash(block.StateRoot),
BodyRoot: common.Hash(block.Body.HashTreeRoot(configs.Mainnet, tree.GetHashFn())),
}
} }
type testHeadTracker struct { type testHeadTracker struct {
@ -151,9 +140,10 @@ func (h *testHeadTracker) ValidatedHead() (types.SignedHeader, bool) {
// TODO add test case for finality // TODO add test case for finality
func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) { func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
finalized := types.NewExecutionHeader(new(deneb.ExecutionPayloadHeader))
return types.FinalityUpdate{ return types.FinalityUpdate{
Attested: types.HeaderWithExecProof{Header: h.validated.Header}, Attested: types.HeaderWithExecProof{Header: h.validated.Header},
Finalized: types.HeaderWithExecProof{PayloadHeader: &capella.ExecutionPayloadHeader{}}, Finalized: types.HeaderWithExecProof{PayloadHeader: finalized},
Signature: h.validated.Signature, Signature: h.validated.Signature,
SignatureSlot: h.validated.SignatureSlot, SignatureSlot: h.validated.SignatureSlot,
}, h.validated.Header != (types.Header{}) }, h.validated.Header != (types.Header{})

View file

@ -28,14 +28,20 @@ import (
"github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
type Client struct { type Client struct {
scheduler *request.Scheduler urls []string
chainHeadFeed *event.Feed customHeader map[string]string
urls []string chainConfig *lightClientConfig
customHeader map[string]string scheduler *request.Scheduler
blockSync *beaconBlockSync
engineRPC *rpc.Client
chainHeadSub event.Subscription
engineClient *engineClient
} }
func NewClient(ctx *cli.Context) *Client { func NewClient(ctx *cli.Context) *Client {
@ -53,6 +59,7 @@ func NewClient(ctx *cli.Context) *Client {
} }
customHeader[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1]) customHeader[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
} }
// create data structures // create data structures
var ( var (
db = memorydb.New() db = memorydb.New()
@ -63,11 +70,10 @@ func NewClient(ctx *cli.Context) *Client {
headSync := sync.NewHeadSync(headTracker, committeeChain) headSync := sync.NewHeadSync(headTracker, committeeChain)
// set up scheduler and sync modules // set up scheduler and sync modules
chainHeadFeed := new(event.Feed)
scheduler := request.NewScheduler() scheduler := request.NewScheduler()
checkpointInit := sync.NewCheckpointInit(committeeChain, chainConfig.Checkpoint) checkpointInit := sync.NewCheckpointInit(committeeChain, chainConfig.Checkpoint)
forwardSync := sync.NewForwardUpdateSync(committeeChain) forwardSync := sync.NewForwardUpdateSync(committeeChain)
beaconBlockSync := newBeaconBlockSync(headTracker, chainHeadFeed) beaconBlockSync := newBeaconBlockSync(headTracker)
scheduler.RegisterTarget(headTracker) scheduler.RegisterTarget(headTracker)
scheduler.RegisterTarget(committeeChain) scheduler.RegisterTarget(committeeChain)
scheduler.RegisterModule(checkpointInit, "checkpointInit") scheduler.RegisterModule(checkpointInit, "checkpointInit")
@ -76,28 +82,34 @@ func NewClient(ctx *cli.Context) *Client {
scheduler.RegisterModule(beaconBlockSync, "beaconBlockSync") scheduler.RegisterModule(beaconBlockSync, "beaconBlockSync")
return &Client{ return &Client{
scheduler: scheduler, scheduler: scheduler,
urls: ctx.StringSlice(utils.BeaconApiFlag.Name), urls: ctx.StringSlice(utils.BeaconApiFlag.Name),
customHeader: customHeader, customHeader: customHeader,
chainHeadFeed: chainHeadFeed, chainConfig: &chainConfig,
blockSync: beaconBlockSync,
} }
} }
// SubscribeChainHeadEvent allows callers to subscribe a provided channel to new func (c *Client) SetEngineRPC(engine *rpc.Client) {
// head updates. c.engineRPC = engine
func (c *Client) SubscribeChainHeadEvent(ch chan<- types.ChainHeadEvent) event.Subscription {
return c.chainHeadFeed.Subscribe(ch)
} }
func (c *Client) Start() { func (c *Client) Start() error {
headCh := make(chan types.ChainHeadEvent, 16)
c.chainHeadSub = c.blockSync.SubscribeChainHead(headCh)
c.engineClient = startEngineClient(c.chainConfig, c.engineRPC, headCh)
c.scheduler.Start() c.scheduler.Start()
// register server(s)
for _, url := range c.urls { for _, url := range c.urls {
beaconApi := api.NewBeaconLightApi(url, c.customHeader) beaconApi := api.NewBeaconLightApi(url, c.customHeader)
c.scheduler.RegisterServer(request.NewServer(api.NewApiServer(beaconApi), &mclock.System{})) c.scheduler.RegisterServer(request.NewServer(api.NewApiServer(beaconApi), &mclock.System{}))
} }
return nil
} }
func (c *Client) Stop() { func (c *Client) Stop() error {
c.engineClient.stop()
c.chainHeadSub.Unsubscribe()
c.scheduler.Stop() c.scheduler.Stop()
return nil
} }

View file

@ -39,7 +39,8 @@ var (
AddFork("GENESIS", 0, []byte{0, 0, 0, 0}). AddFork("GENESIS", 0, []byte{0, 0, 0, 0}).
AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}).
AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}). AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}).
AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}), AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}).
AddFork("DENEB", 269568, []byte{4, 0, 0, 0}),
Checkpoint: common.HexToHash("0x388be41594ec7d6a6894f18c73f3469f07e2c19a803de4755d335817ed8e2e5a"), Checkpoint: common.HexToHash("0x388be41594ec7d6a6894f18c73f3469f07e2c19a803de4755d335817ed8e2e5a"),
} }
@ -51,7 +52,8 @@ var (
AddFork("GENESIS", 0, []byte{144, 0, 0, 105}). AddFork("GENESIS", 0, []byte{144, 0, 0, 105}).
AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}). AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}).
AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}). AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}).
AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}), AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}).
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}),
Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"), Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"),
} }
@ -63,7 +65,8 @@ var (
AddFork("GENESIS", 0, []byte{0, 0, 16, 32}). AddFork("GENESIS", 0, []byte{0, 0, 16, 32}).
AddFork("ALTAIR", 36660, []byte{1, 0, 16, 32}). AddFork("ALTAIR", 36660, []byte{1, 0, 16, 32}).
AddFork("BELLATRIX", 112260, []byte{2, 0, 16, 32}). AddFork("BELLATRIX", 112260, []byte{2, 0, 16, 32}).
AddFork("CAPELLA", 162304, []byte{3, 0, 16, 32}), AddFork("CAPELLA", 162304, []byte{3, 0, 16, 32}).
AddFork("DENEB", 231680, []byte{4, 0, 16, 32}),
Checkpoint: common.HexToHash("0x53a0f4f0a378e2c4ae0a9ee97407eb69d0d737d8d8cd0a5fb1093f42f7b81c49"), Checkpoint: common.HexToHash("0x53a0f4f0a378e2c4ae0a9ee97407eb69d0d737d8d8cd0a5fb1093f42f7b81c49"),
} }
) )

View file

@ -0,0 +1,147 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package blsync
import (
"context"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/common"
ctypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
type engineClient struct {
config *lightClientConfig
rpc *rpc.Client
rootCtx context.Context
cancelRoot context.CancelFunc
wg sync.WaitGroup
}
func startEngineClient(config *lightClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient {
ctx, cancel := context.WithCancel(context.Background())
ec := &engineClient{
config: config,
rpc: rpc,
rootCtx: ctx,
cancelRoot: cancel,
}
ec.wg.Add(1)
go ec.updateLoop(headCh)
return ec
}
func (ec *engineClient) stop() {
ec.cancelRoot()
ec.wg.Wait()
}
func (ec *engineClient) updateLoop(headCh <-chan types.ChainHeadEvent) {
defer ec.wg.Done()
for {
select {
case <-ec.rootCtx.Done():
return
case event := <-headCh:
if ec.rpc == nil { // dry run, no engine API specified
log.Info("New execution block retrieved", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "finalized", event.Finalized)
continue
}
fork := ec.config.ForkAtEpoch(event.BeaconHead.Epoch())
forkName := strings.ToLower(fork.Name)
if status, err := ec.callNewPayload(forkName, event); err == nil {
log.Info("Successful NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "status", status)
} else {
log.Error("Failed NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "error", err)
}
if status, err := ec.callForkchoiceUpdated(forkName, event); err == nil {
log.Info("Successful ForkchoiceUpdated", "head", event.Block.Hash(), "status", status)
} else {
log.Error("Failed ForkchoiceUpdated", "head", event.Block.Hash(), "error", err)
}
}
}
}
func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent) (string, error) {
execData := engine.BlockToExecutableData(event.Block, nil, nil).ExecutionPayload
var (
method string
params = []any{execData}
)
switch fork {
case "deneb":
method = "engine_newPayloadV3"
parentBeaconRoot := event.BeaconHead.ParentRoot
blobHashes := collectBlobHashes(event.Block)
params = append(params, blobHashes, parentBeaconRoot)
case "capella":
method = "engine_newPayloadV2"
default:
method = "engine_newPayloadV1"
}
ctx, cancel := context.WithTimeout(ec.rootCtx, time.Second*5)
defer cancel()
var resp engine.PayloadStatusV1
err := ec.rpc.CallContext(ctx, &resp, method, params...)
return resp.Status, err
}
func collectBlobHashes(b *ctypes.Block) []common.Hash {
list := make([]common.Hash, 0)
for _, tx := range b.Transactions() {
list = append(list, tx.BlobHashes()...)
}
return list
}
func (ec *engineClient) callForkchoiceUpdated(fork string, event types.ChainHeadEvent) (string, error) {
update := engine.ForkchoiceStateV1{
HeadBlockHash: event.Block.Hash(),
SafeBlockHash: event.Finalized,
FinalizedBlockHash: event.Finalized,
}
var method string
switch fork {
case "deneb":
method = "engine_forkchoiceUpdatedV3"
case "capella":
method = "engine_forkchoiceUpdatedV2"
default:
method = "engine_forkchoiceUpdatedV1"
}
ctx, cancel := context.WithTimeout(ec.rootCtx, time.Second*5)
defer cancel()
var resp engine.ForkChoiceResponse
err := ec.rpc.CallContext(ctx, &resp, method, update, nil)
return resp.PayloadStatus.Status, err
}

View file

@ -30,9 +30,6 @@ import (
"github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/protolambda/zrnt/eth2/beacon/capella"
"github.com/protolambda/zrnt/eth2/configs"
"github.com/protolambda/ztyp/tree"
) )
var ( var (
@ -68,9 +65,9 @@ type jsonBeaconHeader struct {
} }
type jsonHeaderWithExecProof struct { type jsonHeaderWithExecProof struct {
Beacon types.Header `json:"beacon"` Beacon types.Header `json:"beacon"`
Execution *capella.ExecutionPayloadHeader `json:"execution"` Execution json.RawMessage `json:"execution"`
ExecutionBranch merkle.Values `json:"execution_branch"` ExecutionBranch merkle.Values `json:"execution_branch"`
} }
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
@ -149,7 +146,7 @@ func (api *BeaconLightApi) httpGetf(format string, params ...any) ([]byte, error
return api.httpGet(fmt.Sprintf(format, params...)) return api.httpGet(fmt.Sprintf(format, params...))
} }
// GetBestUpdateAndCommittee fetches and validates LightClientUpdate for given // GetBestUpdatesAndCommittees fetches and validates LightClientUpdate for given
// period and full serialized committee for the next period (committee root hash // period and full serialized committee for the next period (committee root hash
// equals update.NextSyncCommitteeRoot). // equals update.NextSyncCommitteeRoot).
// Note that the results are validated but the update signature should be verified // Note that the results are validated but the update signature should be verified
@ -244,33 +241,44 @@ func (api *BeaconLightApi) GetFinalityUpdate() (types.FinalityUpdate, error) {
func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) { func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
var data struct { var data struct {
Data struct { Version string
Data struct {
Attested jsonHeaderWithExecProof `json:"attested_header"` Attested jsonHeaderWithExecProof `json:"attested_header"`
Finalized jsonHeaderWithExecProof `json:"finalized_header"` Finalized jsonHeaderWithExecProof `json:"finalized_header"`
FinalityBranch merkle.Values `json:"finality_branch"` FinalityBranch merkle.Values `json:"finality_branch"`
Aggregate types.SyncAggregate `json:"sync_aggregate"` Aggregate types.SyncAggregate `json:"sync_aggregate"`
SignatureSlot common.Decimal `json:"signature_slot"` SignatureSlot common.Decimal `json:"signature_slot"`
} `json:"data"` }
} }
if err := json.Unmarshal(enc, &data); err != nil { if err := json.Unmarshal(enc, &data); err != nil {
return types.FinalityUpdate{}, err return types.FinalityUpdate{}, err
} }
// Decode the execution payload headers.
attestedExecHeader, err := types.ExecutionHeaderFromJSON(data.Version, data.Data.Attested.Execution)
if err != nil {
return types.FinalityUpdate{}, fmt.Errorf("invalid attested header: %v", err)
}
finalizedExecHeader, err := types.ExecutionHeaderFromJSON(data.Version, data.Data.Finalized.Execution)
if err != nil {
return types.FinalityUpdate{}, fmt.Errorf("invalid finalized header: %v", err)
}
// Perform sanity checks.
if len(data.Data.Aggregate.Signers) != params.SyncCommitteeBitmaskSize { if len(data.Data.Aggregate.Signers) != params.SyncCommitteeBitmaskSize {
return types.FinalityUpdate{}, errors.New("invalid sync_committee_bits length") return types.FinalityUpdate{}, errors.New("invalid sync_committee_bits length")
} }
if len(data.Data.Aggregate.Signature) != params.BLSSignatureSize { if len(data.Data.Aggregate.Signature) != params.BLSSignatureSize {
return types.FinalityUpdate{}, errors.New("invalid sync_committee_signature length") return types.FinalityUpdate{}, errors.New("invalid sync_committee_signature length")
} }
return types.FinalityUpdate{ return types.FinalityUpdate{
Attested: types.HeaderWithExecProof{ Attested: types.HeaderWithExecProof{
Header: data.Data.Attested.Beacon, Header: data.Data.Attested.Beacon,
PayloadHeader: data.Data.Attested.Execution, PayloadHeader: attestedExecHeader,
PayloadBranch: data.Data.Attested.ExecutionBranch, PayloadBranch: data.Data.Attested.ExecutionBranch,
}, },
Finalized: types.HeaderWithExecProof{ Finalized: types.HeaderWithExecProof{
Header: data.Data.Finalized.Beacon, Header: data.Data.Finalized.Beacon,
PayloadHeader: data.Data.Finalized.Execution, PayloadHeader: finalizedExecHeader,
PayloadBranch: data.Data.Finalized.ExecutionBranch, PayloadBranch: data.Data.Finalized.ExecutionBranch,
}, },
FinalityBranch: data.Data.FinalityBranch, FinalityBranch: data.Data.FinalityBranch,
@ -359,27 +367,30 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
return checkpoint, nil return checkpoint, nil
} }
func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*capella.BeaconBlock, error) { func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*types.BeaconBlock, error) {
resp, err := api.httpGetf("/eth/v2/beacon/blocks/0x%x", blockRoot) resp, err := api.httpGetf("/eth/v2/beacon/blocks/0x%x", blockRoot)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var beaconBlockMessage struct { var beaconBlockMessage struct {
Data struct { Version string
Message capella.BeaconBlock `json:"message"` Data struct {
} `json:"data"` Message json.RawMessage `json:"message"`
}
} }
if err := json.Unmarshal(resp, &beaconBlockMessage); err != nil { if err := json.Unmarshal(resp, &beaconBlockMessage); err != nil {
return nil, fmt.Errorf("invalid block json data: %v", err) return nil, fmt.Errorf("invalid block json data: %v", err)
} }
beaconBlock := new(capella.BeaconBlock) block, err := types.BlockFromJSON(beaconBlockMessage.Version, beaconBlockMessage.Data.Message)
*beaconBlock = beaconBlockMessage.Data.Message if err != nil {
root := common.Hash(beaconBlock.HashTreeRoot(configs.Mainnet, tree.GetHashFn())) return nil, err
if root != blockRoot {
return nil, fmt.Errorf("Beacon block root hash mismatch (expected: %x, got: %x)", blockRoot, root)
} }
return beaconBlock, nil computedRoot := block.Root()
if computedRoot != blockRoot {
return nil, fmt.Errorf("Beacon block root hash mismatch (expected: %x, got: %x)", blockRoot, computedRoot)
}
return block, nil
} }
func decodeHeadEvent(enc []byte) (uint64, common.Hash, error) { func decodeHeadEvent(enc []byte) (uint64, common.Hash, error) {
@ -456,7 +467,7 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
select { select {
case event, ok := <-stream.Events: case event, ok := <-stream.Events:
if !ok { if !ok {
break return
} }
switch event.Event() { switch event.Event() {
case "head": case "head":
@ -482,7 +493,7 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
} }
case err, ok := <-stream.Errors: case err, ok := <-stream.Errors:
if !ok { if !ok {
break return
} }
listener.OnError(err) listener.OnError(err)
} }

View file

@ -73,7 +73,7 @@ func TestValidatedHead(t *testing.T) {
ts.AddServer(testServer3, 1) ts.AddServer(testServer3, 1)
ts.ServerEvent(EvNewSignedHead, testServer3, testSHead4) ts.ServerEvent(EvNewSignedHead, testServer3, testSHead4)
ts.Run(4) ts.Run(4)
// future period annonced heads should be queued // future period announced heads should be queued
ht.ExpValidated(t, 4, nil) ht.ExpValidated(t, 4, nil)
chain.SetNextSyncPeriod(2) chain.SetNextSyncPeriod(2)

View file

@ -0,0 +1,110 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package types
import (
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/protolambda/zrnt/eth2/beacon/capella"
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
"github.com/protolambda/zrnt/eth2/configs"
"github.com/protolambda/ztyp/tree"
)
type blockObject interface {
HashTreeRoot(spec *zrntcommon.Spec, hFn tree.HashFn) zrntcommon.Root
Header(spec *zrntcommon.Spec) *zrntcommon.BeaconBlockHeader
}
// BeaconBlock represents a full block in the beacon chain.
type BeaconBlock struct {
blockObj blockObject
}
// BlockFromJSON decodes a beacon block from JSON.
func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) {
var obj blockObject
switch forkName {
case "deneb":
obj = new(deneb.BeaconBlock)
case "capella":
obj = new(capella.BeaconBlock)
default:
return nil, fmt.Errorf("unsupported fork: " + forkName)
}
if err := json.Unmarshal(data, obj); err != nil {
return nil, err
}
return &BeaconBlock{obj}, nil
}
// NewBeaconBlock wraps a ZRNT block.
func NewBeaconBlock(obj blockObject) *BeaconBlock {
switch obj := obj.(type) {
case *capella.BeaconBlock:
return &BeaconBlock{obj}
case *deneb.BeaconBlock:
return &BeaconBlock{obj}
default:
panic(fmt.Errorf("unsupported block type %T", obj))
}
}
// Slot returns the slot number of the block.
func (b *BeaconBlock) Slot() uint64 {
switch obj := b.blockObj.(type) {
case *capella.BeaconBlock:
return uint64(obj.Slot)
case *deneb.BeaconBlock:
return uint64(obj.Slot)
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
}
// ExecutionPayload parses and returns the execution payload of the block.
func (b *BeaconBlock) ExecutionPayload() (*types.Block, error) {
switch obj := b.blockObj.(type) {
case *capella.BeaconBlock:
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot)
case *deneb.BeaconBlock:
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot)
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
}
// Header returns the block's header data.
func (b *BeaconBlock) Header() Header {
switch obj := b.blockObj.(type) {
case *capella.BeaconBlock:
return headerFromZRNT(obj.Header(configs.Mainnet))
case *deneb.BeaconBlock:
return headerFromZRNT(obj.Header(configs.Mainnet))
default:
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
}
}
// Root computes the SSZ root hash of the block.
func (b *BeaconBlock) Root() common.Hash {
return common.Hash(b.blockObj.HashTreeRoot(configs.Mainnet, tree.GetHashFn()))
}

View file

@ -0,0 +1,77 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package types
import (
"os"
"path/filepath"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestBlockFromJSON(t *testing.T) {
type blocktest struct {
file string
version string
wantSlot uint64
wantBlockNumber uint64
wantBlockHash common.Hash
}
tests := []blocktest{
{
file: "block_deneb.json",
version: "deneb",
wantSlot: 8631513,
wantBlockNumber: 19431837,
wantBlockHash: common.HexToHash("0x4cf7d9108fc01b50023ab7cab9b372a96068fddcadec551630393b65acb1f34c"),
},
{
file: "block_capella.json",
version: "capella",
wantSlot: 7378495,
wantBlockNumber: 18189758,
wantBlockHash: common.HexToHash("0x802acf5c350f4252e31d83c431fcb259470250fa0edf49e8391cfee014239820"),
},
}
for _, test := range tests {
t.Run(test.file, func(t *testing.T) {
data, err := os.ReadFile(filepath.Join("testdata", test.file))
if err != nil {
t.Fatal(err)
}
beaconBlock, err := BlockFromJSON(test.version, data)
if err != nil {
t.Fatal(err)
}
if beaconBlock.Slot() != test.wantSlot {
t.Errorf("wrong slot number %d", beaconBlock.Slot())
}
execBlock, err := beaconBlock.ExecutionPayload()
if err != nil {
t.Fatalf("payload extraction failed: %v", err)
}
if execBlock.NumberU64() != test.wantBlockNumber {
t.Errorf("wrong block number: %v", execBlock.NumberU64())
}
if execBlock.Hash() != test.wantBlockHash {
t.Errorf("wrong block hash: %v", execBlock.Hash())
}
})
}
}

View file

@ -37,7 +37,7 @@ const syncCommitteeDomain = 7
// Fork describes a single beacon chain fork and also stores the calculated // Fork describes a single beacon chain fork and also stores the calculated
// signature domain used after this fork. // signature domain used after this fork.
type Fork struct { type Fork struct {
// Name of the fork in the chain config (config.yaml) file{ // Name of the fork in the chain config (config.yaml) file
Name string Name string
// Epoch when given fork version is activated // Epoch when given fork version is activated
@ -110,6 +110,16 @@ type ChainConfig struct {
Forks Forks Forks Forks
} }
// ForkAtEpoch returns the latest active fork at the given epoch.
func (c *ChainConfig) ForkAtEpoch(epoch uint64) Fork {
for i := len(c.Forks) - 1; i >= 0; i-- {
if c.Forks[i].Epoch <= epoch {
return *c.Forks[i]
}
}
return Fork{}
}
// AddFork adds a new item to the list of forks. // AddFork adds a new item to the list of forks.
func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig { func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
fork := &Fork{ fork := &Fork{

View file

@ -0,0 +1,80 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package types
import (
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/beacon/merkle"
"github.com/ethereum/go-ethereum/common"
"github.com/protolambda/zrnt/eth2/beacon/capella"
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
"github.com/protolambda/ztyp/tree"
)
type headerObject interface {
HashTreeRoot(hFn tree.HashFn) zrntcommon.Root
}
type ExecutionHeader struct {
obj headerObject
}
// HeaderFromJSON decodes an execution header from JSON data provided by
// the beacon chain API.
func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, error) {
var obj headerObject
switch forkName {
case "capella":
obj = new(capella.ExecutionPayloadHeader)
case "deneb":
obj = new(deneb.ExecutionPayloadHeader)
default:
return nil, fmt.Errorf("unsupported fork: " + forkName)
}
if err := json.Unmarshal(data, obj); err != nil {
return nil, err
}
return &ExecutionHeader{obj: obj}, nil
}
func NewExecutionHeader(obj headerObject) *ExecutionHeader {
switch obj.(type) {
case *capella.ExecutionPayloadHeader:
case *deneb.ExecutionPayloadHeader:
default:
panic(fmt.Errorf("unsupported ExecutionPayloadHeader type %T", obj))
}
return &ExecutionHeader{obj: obj}
}
func (eh *ExecutionHeader) PayloadRoot() merkle.Value {
return merkle.Value(eh.obj.HashTreeRoot(tree.GetHashFn()))
}
func (eh *ExecutionHeader) BlockHash() common.Hash {
switch obj := eh.obj.(type) {
case *capella.ExecutionPayloadHeader:
return common.Hash(obj.BlockHash)
case *deneb.ExecutionPayloadHeader:
return common.Hash(obj.BlockHash)
default:
panic(fmt.Errorf("unsupported ExecutionPayloadHeader type %T", obj))
}
}

View file

@ -0,0 +1,144 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package types
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"github.com/protolambda/zrnt/eth2/beacon/capella"
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
"github.com/protolambda/zrnt/eth2/beacon/deneb"
)
type payloadType interface {
*capella.ExecutionPayload | *deneb.ExecutionPayload
}
// convertPayload converts a beacon chain execution payload to types.Block.
func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*types.Block, error) {
var (
header types.Header
transactions []*types.Transaction
withdrawals []*types.Withdrawal
expectedHash [32]byte
err error
)
switch p := any(payload).(type) {
case *capella.ExecutionPayload:
convertCapellaHeader(p, &header)
transactions, err = convertTransactions(p.Transactions, &header)
if err != nil {
return nil, err
}
withdrawals = convertWithdrawals(p.Withdrawals, &header)
expectedHash = p.BlockHash
case *deneb.ExecutionPayload:
convertDenebHeader(p, common.Hash(*parentRoot), &header)
transactions, err = convertTransactions(p.Transactions, &header)
if err != nil {
return nil, err
}
withdrawals = convertWithdrawals(p.Withdrawals, &header)
expectedHash = p.BlockHash
default:
panic("unsupported block type")
}
block := types.NewBlockWithHeader(&header)
block = block.WithBody(transactions, nil)
block = block.WithWithdrawals(withdrawals)
hash := block.Hash()
if hash != expectedHash {
return block, fmt.Errorf("Sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash)
}
return block, nil
}
func convertCapellaHeader(payload *capella.ExecutionPayload, h *types.Header) {
// note: h.TxHash is set in convertTransactions
h.ParentHash = common.Hash(payload.ParentHash)
h.UncleHash = types.EmptyUncleHash
h.Coinbase = common.Address(payload.FeeRecipient)
h.Root = common.Hash(payload.StateRoot)
h.ReceiptHash = common.Hash(payload.ReceiptsRoot)
h.Bloom = types.Bloom(payload.LogsBloom)
h.Difficulty = common.Big0
h.Number = new(big.Int).SetUint64(uint64(payload.BlockNumber))
h.GasLimit = uint64(payload.GasLimit)
h.GasUsed = uint64(payload.GasUsed)
h.Time = uint64(payload.Timestamp)
h.Extra = []byte(payload.ExtraData)
h.MixDigest = common.Hash(payload.PrevRandao)
h.Nonce = types.BlockNonce{}
h.BaseFee = (*uint256.Int)(&payload.BaseFeePerGas).ToBig()
}
func convertDenebHeader(payload *deneb.ExecutionPayload, parentRoot common.Hash, h *types.Header) {
// note: h.TxHash is set in convertTransactions
h.ParentHash = common.Hash(payload.ParentHash)
h.UncleHash = types.EmptyUncleHash
h.Coinbase = common.Address(payload.FeeRecipient)
h.Root = common.Hash(payload.StateRoot)
h.ReceiptHash = common.Hash(payload.ReceiptsRoot)
h.Bloom = types.Bloom(payload.LogsBloom)
h.Difficulty = common.Big0
h.Number = new(big.Int).SetUint64(uint64(payload.BlockNumber))
h.GasLimit = uint64(payload.GasLimit)
h.GasUsed = uint64(payload.GasUsed)
h.Time = uint64(payload.Timestamp)
h.Extra = []byte(payload.ExtraData)
h.MixDigest = common.Hash(payload.PrevRandao)
h.Nonce = types.BlockNonce{}
h.BaseFee = (*uint256.Int)(&payload.BaseFeePerGas).ToBig()
// new in deneb
h.BlobGasUsed = (*uint64)(&payload.BlobGasUsed)
h.ExcessBlobGas = (*uint64)(&payload.ExcessBlobGas)
h.ParentBeaconRoot = &parentRoot
}
func convertTransactions(list zrntcommon.PayloadTransactions, execHeader *types.Header) ([]*types.Transaction, error) {
txs := make([]*types.Transaction, len(list))
for i, opaqueTx := range list {
var tx types.Transaction
if err := tx.UnmarshalBinary(opaqueTx); err != nil {
return nil, fmt.Errorf("failed to parse tx %d: %v", i, err)
}
txs[i] = &tx
}
execHeader.TxHash = types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil))
return txs, nil
}
func convertWithdrawals(list zrntcommon.Withdrawals, execHeader *types.Header) []*types.Withdrawal {
withdrawals := make([]*types.Withdrawal, len(list))
for i, w := range list {
withdrawals[i] = &types.Withdrawal{
Index: uint64(w.Index),
Validator: uint64(w.ValidatorIndex),
Address: common.Address(w.Address),
Amount: uint64(w.Amount),
}
}
wroot := types.DeriveSha(types.Withdrawals(withdrawals), trie.NewStackTrie(nil))
execHeader.WithdrawalsHash = &wroot
return withdrawals
}

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/beacon/merkle" "github.com/ethereum/go-ethereum/beacon/merkle"
"github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/params"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
) )
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go //go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
@ -57,6 +58,16 @@ type Header struct {
BodyRoot common.Hash `gencodec:"required" json:"body_root"` BodyRoot common.Hash `gencodec:"required" json:"body_root"`
} }
func headerFromZRNT(zh *zrntcommon.BeaconBlockHeader) Header {
return Header{
Slot: uint64(zh.Slot),
ProposerIndex: uint64(zh.ProposerIndex),
ParentRoot: common.Hash(zh.ParentRoot),
StateRoot: common.Hash(zh.StateRoot),
BodyRoot: common.Hash(zh.BodyRoot),
}
}
// headerMarshaling is a field type overrides for gencodec. // headerMarshaling is a field type overrides for gencodec.
type headerMarshaling struct { type headerMarshaling struct {
Slot common.Decimal Slot common.Decimal

View file

@ -20,12 +20,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/beacon/merkle" "github.com/ethereum/go-ethereum/beacon/merkle"
"github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/params"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/protolambda/zrnt/eth2/beacon/capella" "github.com/ethereum/go-ethereum/core/types"
"github.com/protolambda/ztyp/tree"
) )
// HeadInfo represents an unvalidated new head announcement. // HeadInfo represents an unvalidated new head announcement.
@ -146,12 +144,12 @@ func (u UpdateScore) BetterThan(w UpdateScore) bool {
type HeaderWithExecProof struct { type HeaderWithExecProof struct {
Header Header
PayloadHeader *capella.ExecutionPayloadHeader PayloadHeader *ExecutionHeader
PayloadBranch merkle.Values PayloadBranch merkle.Values
} }
func (h *HeaderWithExecProof) Validate() error { func (h *HeaderWithExecProof) Validate() error {
payloadRoot := merkle.Value(h.PayloadHeader.HashTreeRoot(tree.GetHashFn())) payloadRoot := h.PayloadHeader.PayloadRoot()
return merkle.VerifyProof(h.BodyRoot, params.BodyIndexExecPayload, h.PayloadBranch, payloadRoot) return merkle.VerifyProof(h.BodyRoot, params.BodyIndexExecPayload, h.PayloadBranch, payloadRoot)
} }
@ -187,6 +185,7 @@ func (u *FinalityUpdate) Validate() error {
// latest accepted head of the beacon chain, along with the hash of the latest // latest accepted head of the beacon chain, along with the hash of the latest
// finalized execution block. // finalized execution block.
type ChainHeadEvent struct { type ChainHeadEvent struct {
HeadBlock *engine.ExecutableData BeaconHead Header
Finalized common.Hash Block *types.Block
Finalized common.Hash
} }

1703
beacon/types/testdata/block_capella.json vendored Normal file

File diff suppressed because it is too large Load diff

2644
beacon/types/testdata/block_deneb.json vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -5,22 +5,22 @@
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/ # https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
# version:golang 1.21.6 # version:golang 1.22.1
# https://go.dev/dl/ # https://go.dev/dl/
124926a62e45f78daabbaedb9c011d97633186a33c238ffc1e25320c02046248 go1.21.6.src.tar.gz 79c9b91d7f109515a25fc3ecdaad125d67e6bdb54f6d4d98580f46799caea321 go1.22.1.src.tar.gz
31d6ecca09010ab351e51343a5af81d678902061fee871f912bdd5ef4d778850 go1.21.6.darwin-amd64.tar.gz 3bc971772f4712fec0364f4bc3de06af22a00a12daab10b6f717fdcd13156cc0 go1.22.1.darwin-amd64.tar.gz
0ff541fb37c38e5e5c5bcecc8f4f43c5ffd5e3a6c33a5d3e4003ded66fcfb331 go1.21.6.darwin-arm64.tar.gz f6a9cec6b8a002fcc9c0ee24ec04d67f430a52abc3cfd613836986bcc00d8383 go1.22.1.darwin-arm64.tar.gz
a1d1a149b34bf0f53965a237682c6da1140acabb131bf0e597240e4a140b0e5e go1.21.6.freebsd-386.tar.gz 99f81c10d5a3f8a886faf8fa86aaa2aaf929fbed54a972ae5eec3c5e0bdb961a go1.22.1.freebsd-386.tar.gz
de59e1217e4398b1522eed8dddabab2fa1b97aecbdca3af08e34832b4f0e3f81 go1.21.6.freebsd-amd64.tar.gz 51c614ddd92ee4a9913a14c39bf80508d9cfba08561f24d2f075fd00f3cfb067 go1.22.1.freebsd-amd64.tar.gz
05d09041b5a1193c14e4b2db3f7fcc649b236c567f5eb93305c537851b72dd95 go1.21.6.linux-386.tar.gz 8484df36d3d40139eaf0fe5e647b006435d826cc12f9ae72973bf7ec265e0ae4 go1.22.1.linux-386.tar.gz
3f934f40ac360b9c01f616a9aa1796d227d8b0328bf64cb045c7b8c4ee9caea4 go1.21.6.linux-amd64.tar.gz aab8e15785c997ae20f9c88422ee35d962c4562212bb0f879d052a35c8307c7f go1.22.1.linux-amd64.tar.gz
e2e8aa88e1b5170a0d495d7d9c766af2b2b6c6925a8f8956d834ad6b4cacbd9a go1.21.6.linux-arm64.tar.gz e56685a245b6a0c592fc4a55f0b7803af5b3f827aaa29feab1f40e491acf35b8 go1.22.1.linux-arm64.tar.gz
6a8eda6cc6a799ff25e74ce0c13fdc1a76c0983a0bb07c789a2a3454bf6ec9b2 go1.21.6.linux-armv6l.tar.gz 8cb7a90e48c20daed39a6ac8b8a40760030ba5e93c12274c42191d868687c281 go1.22.1.linux-armv6l.tar.gz
e872b1e9a3f2f08fd4554615a32ca9123a4ba877ab6d19d36abc3424f86bc07f go1.21.6.linux-ppc64le.tar.gz ac775e19d93cc1668999b77cfe8c8964abfbc658718feccfe6e0eb87663cd668 go1.22.1.linux-ppc64le.tar.gz
92894d0f732d3379bc414ffdd617eaadad47e1d72610e10d69a1156db03fc052 go1.21.6.linux-s390x.tar.gz 7bb7dd8e10f95c9a4cc4f6bef44c816a6e7c9e03f56ac6af6efbb082b19b379f go1.22.1.linux-s390x.tar.gz
65b38857135cf45c80e1d267e0ce4f80fe149326c68835217da4f2da9b7943fe go1.21.6.windows-386.zip 0c5ebb7eb39b7884ec99f92b425d4c03a96a72443562aafbf6e7d15c42a3108a go1.22.1.windows-386.zip
27ac9dd6e66fb3fd0acfa6792ff053c86e7d2c055b022f4b5d53bfddec9e3301 go1.21.6.windows-amd64.zip cf9c66a208a106402a527f5b956269ca506cfe535fc388e828d249ea88ed28ba go1.22.1.windows-amd64.zip
b93aff8f3c882c764c66a39b7a1483b0460e051e9992bf3435479129e5051bcd go1.21.6.windows-arm64.zip 85b8511b298c9f4199ecae26afafcc3d46155bac934d43f2357b9224bcaa310f go1.22.1.windows-arm64.zip
# version:golangci 1.55.2 # version:golangci 1.55.2
# https://github.com/golangci/golangci-lint/releases/ # https://github.com/golangci/golangci-lint/releases/

View file

@ -46,13 +46,12 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"text/template" "text/template"
"time" "time"
"golang.org/x/exp/slices"
) )
var ( var (

View file

@ -1,69 +0,0 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package main
import (
"context"
"time"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
func updateEngineApi(client *rpc.Client, headCh chan types.ChainHeadEvent) {
for event := range headCh {
if client == nil { // dry run, no engine API specified
log.Info("New execution block retrieved", "block number", event.HeadBlock.Number, "block hash", event.HeadBlock.BlockHash, "finalized block hash", event.Finalized)
} else {
if status, err := callNewPayloadV2(client, event.HeadBlock); err == nil {
log.Info("Successful NewPayload", "block number", event.HeadBlock.Number, "block hash", event.HeadBlock.BlockHash, "status", status)
} else {
log.Error("Failed NewPayload", "block number", event.HeadBlock.Number, "block hash", event.HeadBlock.BlockHash, "error", err)
}
if status, err := callForkchoiceUpdatedV1(client, event.HeadBlock.BlockHash, event.Finalized); err == nil {
log.Info("Successful ForkchoiceUpdated", "head", event.HeadBlock.BlockHash, "status", status)
} else {
log.Error("Failed ForkchoiceUpdated", "head", event.HeadBlock.BlockHash, "error", err)
}
}
}
}
func callNewPayloadV2(client *rpc.Client, execData *engine.ExecutableData) (string, error) {
var resp engine.PayloadStatusV1
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
err := client.CallContext(ctx, &resp, "engine_newPayloadV2", execData)
cancel()
return resp.Status, err
}
func callForkchoiceUpdatedV1(client *rpc.Client, headHash, finalizedHash common.Hash) (string, error) {
var resp engine.ForkChoiceResponse
update := engine.ForkchoiceStateV1{
HeadBlockHash: headHash,
SafeBlockHash: finalizedHash,
FinalizedBlockHash: finalizedHash,
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
err := client.CallContext(ctx, &resp, "engine_forkchoiceUpdatedV1", update, nil)
cancel()
return resp.PayloadStatus.Status, err
}

View file

@ -23,7 +23,6 @@ import (
"os" "os"
"github.com/ethereum/go-ethereum/beacon/blsync" "github.com/ethereum/go-ethereum/beacon/blsync"
"github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -87,16 +86,14 @@ func sync(ctx *cli.Context) error {
verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name)) verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name))
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, verbosity, usecolor))) log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, verbosity, usecolor)))
headCh := make(chan types.ChainHeadEvent, 16) // set up blsync
client := blsync.NewClient(ctx) client := blsync.NewClient(ctx)
sub := client.SubscribeChainHeadEvent(headCh) client.SetEngineRPC(makeRPCClient(ctx))
go updateEngineApi(makeRPCClient(ctx), headCh)
client.Start() client.Start()
// run until stopped // run until stopped
<-ctx.Done() <-ctx.Done()
client.Stop() client.Stop()
sub.Unsubscribe()
close(headCh)
return nil return nil
} }

View file

@ -20,6 +20,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"slices"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -32,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/dnsdisc" "github.com/ethereum/go-ethereum/p2p/dnsdisc"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
) )
const ( const (

View file

@ -183,8 +183,8 @@ var attrFormatters = map[string]func(rlp.RawValue) (string, bool){
} }
func formatAttrRaw(v rlp.RawValue) (string, bool) { func formatAttrRaw(v rlp.RawValue) (string, bool) {
s := hex.EncodeToString(v) content, _, err := rlp.SplitString(v)
return s, true return hex.EncodeToString(content), err == nil
} }
func formatAttrString(v rlp.RawValue) (string, bool) { func formatAttrString(v rlp.RawValue) (string, bool) {

View file

@ -27,6 +27,7 @@ import (
"math/big" "math/big"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strings" "strings"
@ -40,7 +41,6 @@ import (
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/exp/slices"
) )
// Chain is a lightweight blockchain-like store which can read a hivechain // Chain is a lightweight blockchain-like store which can read a hivechain

View file

@ -21,11 +21,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"slices"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"golang.org/x/exp/slices"
) )
const jsonIndent = " " const jsonIndent = " "

View file

@ -34,7 +34,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -123,7 +123,7 @@ type rejectedTx struct {
// Apply applies a set of transactions to a pre-state // Apply applies a set of transactions to a pre-state
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txIt txIterator, miningReward int64, txIt txIterator, miningReward int64,
getTracerFn func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) { getTracerFn func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
// Capture errors for BLOCKHASH operation, if we haven't been supplied the // Capture errors for BLOCKHASH operation, if we haven't been supplied the
// required blockhashes // required blockhashes
var hashError error var hashError error
@ -173,7 +173,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
// Calculate the BlobBaseFee // Calculate the BlobBaseFee
var excessBlobGas uint64 var excessBlobGas uint64
if pre.Env.ExcessBlobGas != nil { if pre.Env.ExcessBlobGas != nil {
excessBlobGas := *pre.Env.ExcessBlobGas excessBlobGas = *pre.Env.ExcessBlobGas
vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
} else { } else {
// If it is not explicitly defined, but we have the parent values, we try // If it is not explicitly defined, but we have the parent values, we try
@ -422,7 +422,7 @@ func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime
return ethash.CalcDifficulty(config, currentTime, parent) return ethash.CalcDifficulty(config, currentTime, parent)
} }
func writeTraceResult(tracer *directory.Tracer, f io.WriteCloser) error { func writeTraceResult(tracer *tracers.Tracer, f io.WriteCloser) error {
defer f.Close() defer f.Close()
result, err := tracer.GetResult() result, err := tracer.GetResult()
if err != nil || result == nil { if err != nil || result == nil {

View file

@ -31,7 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -81,7 +81,7 @@ type input struct {
} }
func Transition(ctx *cli.Context) error { func Transition(ctx *cli.Context) error {
var getTracer = func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error) { return nil, nil, nil } var getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) { return nil, nil, nil }
baseDir, err := createBasedir(ctx) baseDir, err := createBasedir(ctx)
if err != nil { if err != nil {
@ -96,13 +96,13 @@ func Transition(ctx *cli.Context) error {
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name), EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
Debug: true, Debug: true,
} }
getTracer = func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error) { getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String()))) traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
if err != nil { if err != nil {
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
} }
logger := logger.NewJSONLogger(logConfig, traceFile) logger := logger.NewJSONLogger(logConfig, traceFile)
tracer := &directory.Tracer{ tracer := &tracers.Tracer{
Hooks: logger, Hooks: logger,
// jsonLogger streams out result to file. // jsonLogger streams out result to file.
GetResult: func() (json.RawMessage, error) { return nil, nil }, GetResult: func() (json.RawMessage, error) { return nil, nil },
@ -115,12 +115,12 @@ func Transition(ctx *cli.Context) error {
if ctx.IsSet(TraceTracerConfigFlag.Name) { if ctx.IsSet(TraceTracerConfigFlag.Name) {
config = []byte(ctx.String(TraceTracerConfigFlag.Name)) config = []byte(ctx.String(TraceTracerConfigFlag.Name))
} }
getTracer = func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error) { getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String()))) traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
if err != nil { if err != nil {
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
} }
tracer, err := directory.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config) tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config)
if err != nil { if err != nil {
return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err)) return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
} }

View file

@ -67,7 +67,7 @@ func stateTestCmd(ctx *cli.Context) error {
} }
// Load the test content from the input file // Load the test content from the input file
if len(ctx.Args().First()) != 0 { if len(ctx.Args().First()) != 0 {
return runStateTest(ctx.Args().First(), cfg, ctx.Bool(MachineFlag.Name), ctx.Bool(DumpFlag.Name)) return runStateTest(ctx.Args().First(), cfg, ctx.Bool(DumpFlag.Name))
} }
// Read filenames from stdin and execute back-to-back // Read filenames from stdin and execute back-to-back
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
@ -76,7 +76,7 @@ func stateTestCmd(ctx *cli.Context) error {
if len(fname) == 0 { if len(fname) == 0 {
return nil return nil
} }
if err := runStateTest(fname, cfg, ctx.Bool(MachineFlag.Name), ctx.Bool(DumpFlag.Name)); err != nil { if err := runStateTest(fname, cfg, ctx.Bool(DumpFlag.Name)); err != nil {
return err return err
} }
} }
@ -84,7 +84,7 @@ func stateTestCmd(ctx *cli.Context) error {
} }
// runStateTest loads the state-test given by fname, and executes the test. // runStateTest loads the state-test given by fname, and executes the test.
func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error { func runStateTest(fname string, cfg vm.Config, dump bool) error {
src, err := os.ReadFile(fname) src, err := os.ReadFile(fname)
if err != nil { if err != nil {
return err return err
@ -105,9 +105,7 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
if tstate.StateDB != nil { if tstate.StateDB != nil {
root = tstate.StateDB.IntermediateRoot(false) root = tstate.StateDB.IntermediateRoot(false)
result.Root = &root result.Root = &root
if jsonOut { fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
}
if dump { // Dump any state to aid debugging if dump { // Dump any state to aid debugging
cpy, _ := state.New(root, tstate.StateDB.Database(), nil) cpy, _ := state.New(root, tstate.StateDB.Database(), nil)
dump := cpy.RawDump(nil) dump := cpy.RawDump(nil)

View file

@ -44,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/naoina/toml" "github.com/naoina/toml"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -214,9 +215,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
} }
utils.RegisterFullSyncTester(stack, eth, common.BytesToHash(hex)) utils.RegisterFullSyncTester(stack, eth, common.BytesToHash(hex))
} }
// Start the dev mode if requested, or launch the engine API for
// interacting with external consensus client.
if ctx.IsSet(utils.DeveloperFlag.Name) { if ctx.IsSet(utils.DeveloperFlag.Name) {
// Start dev mode.
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), eth) simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), eth)
if err != nil { if err != nil {
utils.Fatalf("failed to register dev mode catalyst service: %v", err) utils.Fatalf("failed to register dev mode catalyst service: %v", err)
@ -224,8 +225,14 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon) catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
stack.RegisterLifecycle(simBeacon) stack.RegisterLifecycle(simBeacon)
} else if ctx.IsSet(utils.BeaconApiFlag.Name) { } else if ctx.IsSet(utils.BeaconApiFlag.Name) {
stack.RegisterLifecycle(catalyst.NewBlsync(blsync.NewClient(ctx), eth)) // Start blsync mode.
srv := rpc.NewServer()
srv.RegisterName("engine", catalyst.NewConsensusAPI(eth))
blsyncer := blsync.NewClient(ctx)
blsyncer.SetEngineRPC(rpc.DialInProc(srv))
stack.RegisterLifecycle(blsyncer)
} else { } else {
// Launch the engine API for interacting with external consensus client.
err := catalyst.Register(stack, eth) err := catalyst.Register(stack, eth)
if err != nil { if err != nil {
utils.Fatalf("failed to register catalyst service: %v", err) utils.Fatalf("failed to register catalyst service: %v", err)

View file

@ -33,11 +33,14 @@ import (
"github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/console/prompt"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -79,6 +82,7 @@ Remove blockchain and state databases`,
dbExportCmd, dbExportCmd,
dbMetadataCmd, dbMetadataCmd,
dbCheckStateContentCmd, dbCheckStateContentCmd,
dbInspectHistoryCmd,
}, },
} }
dbInspectCmd = &cli.Command{ dbInspectCmd = &cli.Command{
@ -203,6 +207,28 @@ WARNING: This is a low-level operation which may cause database corruption!`,
}, utils.NetworkFlags, utils.DatabaseFlags), }, utils.NetworkFlags, utils.DatabaseFlags),
Description: "Shows metadata about the chain status.", Description: "Shows metadata about the chain status.",
} }
dbInspectHistoryCmd = &cli.Command{
Action: inspectHistory,
Name: "inspect-history",
Usage: "Inspect the state history within block range",
ArgsUsage: "<address> [OPTIONAL <storage-slot>]",
Flags: flags.Merge([]cli.Flag{
utils.SyncModeFlag,
&cli.Uint64Flag{
Name: "start",
Usage: "block number of the range start, zero means earliest history",
},
&cli.Uint64Flag{
Name: "end",
Usage: "block number of the range end(included), zero means latest history",
},
&cli.BoolFlag{
Name: "raw",
Usage: "display the decoded raw state value (otherwise shows rlp-encoded value)",
},
}, utils.NetworkFlags, utils.DatabaseFlags),
Description: "This command queries the history of the account or storage slot within the specified block range",
}
) )
func removeDB(ctx *cli.Context) error { func removeDB(ctx *cli.Context) error {
@ -759,3 +785,145 @@ func showMetaData(ctx *cli.Context) error {
table.Render() table.Render()
return nil return nil
} }
func inspectAccount(db *triedb.Database, start uint64, end uint64, address common.Address, raw bool) error {
stats, err := db.AccountHistory(address, start, end)
if err != nil {
return err
}
fmt.Printf("Account history:\n\taddress: %s\n\tblockrange: [#%d-#%d]\n", address.Hex(), stats.Start, stats.End)
from := stats.Start
for i := 0; i < len(stats.Blocks); i++ {
var content string
if len(stats.Origins[i]) == 0 {
content = "<empty>"
} else {
if !raw {
content = fmt.Sprintf("%#x", stats.Origins[i])
} else {
account := new(types.SlimAccount)
if err := rlp.DecodeBytes(stats.Origins[i], account); err != nil {
panic(err)
}
code := "<nil>"
if len(account.CodeHash) > 0 {
code = fmt.Sprintf("%#x", account.CodeHash)
}
root := "<nil>"
if len(account.Root) > 0 {
root = fmt.Sprintf("%#x", account.Root)
}
content = fmt.Sprintf("nonce: %d, balance: %d, codeHash: %s, root: %s", account.Nonce, account.Balance, code, root)
}
}
fmt.Printf("#%d - #%d: %s\n", from, stats.Blocks[i], content)
from = stats.Blocks[i]
}
return nil
}
func inspectStorage(db *triedb.Database, start uint64, end uint64, address common.Address, slot common.Hash, raw bool) error {
// The hash of storage slot key is utilized in the history
// rather than the raw slot key, make the conversion.
slotHash := crypto.Keccak256Hash(slot.Bytes())
stats, err := db.StorageHistory(address, slotHash, start, end)
if err != nil {
return err
}
fmt.Printf("Storage history:\n\taddress: %s\n\tslot: %s\n\tblockrange: [#%d-#%d]\n", address.Hex(), slot.Hex(), stats.Start, stats.End)
from := stats.Start
for i := 0; i < len(stats.Blocks); i++ {
var content string
if len(stats.Origins[i]) == 0 {
content = "<empty>"
} else {
if !raw {
content = fmt.Sprintf("%#x", stats.Origins[i])
} else {
_, data, _, err := rlp.Split(stats.Origins[i])
if err != nil {
fmt.Printf("Failed to decode storage slot, %v", err)
return err
}
content = fmt.Sprintf("%#x", data)
}
}
fmt.Printf("#%d - #%d: %s\n", from, stats.Blocks[i], content)
from = stats.Blocks[i]
}
return nil
}
func inspectHistory(ctx *cli.Context) error {
if ctx.NArg() == 0 || ctx.NArg() > 2 {
return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
}
var (
address common.Address
slot common.Hash
)
if err := address.UnmarshalText([]byte(ctx.Args().Get(0))); err != nil {
return err
}
if ctx.NArg() > 1 {
if err := slot.UnmarshalText([]byte(ctx.Args().Get(1))); err != nil {
return err
}
}
// Load the databases.
stack, _ := makeConfigNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
triedb := utils.MakeTrieDatabase(ctx, db, false, false, false)
defer triedb.Close()
var (
err error
start uint64 // the id of first history object to query
end uint64 // the id (included) of last history object to query
)
// State histories are identified by state ID rather than block number.
// To address this, load the corresponding block header and perform the
// conversion by this function.
blockToID := func(blockNumber uint64) (uint64, error) {
header := rawdb.ReadHeader(db, rawdb.ReadCanonicalHash(db, blockNumber), blockNumber)
if header == nil {
return 0, fmt.Errorf("block #%d is not existent", blockNumber)
}
id := rawdb.ReadStateID(db, header.Root)
if id == nil {
first, last, err := triedb.HistoryRange()
if err == nil {
return 0, fmt.Errorf("history of block #%d is not existent, available history range: [#%d-#%d]", blockNumber, first, last)
}
return 0, fmt.Errorf("history of block #%d is not existent", blockNumber)
}
return *id, nil
}
// Parse the starting block number for inspection.
startNumber := ctx.Uint64("start")
if startNumber != 0 {
start, err = blockToID(startNumber)
if err != nil {
return err
}
}
// Parse the ending block number for inspection.
endBlock := ctx.Uint64("end")
if endBlock != 0 {
end, err = blockToID(endBlock)
if err != nil {
return err
}
}
// Inspect the state history.
if slot == (common.Hash{}) {
return inspectAccount(triedb, start, end, address, ctx.Bool("raw"))
}
return inspectStorage(triedb, start, end, address, slot, ctx.Bool("raw"))
}

View file

@ -53,7 +53,6 @@ import (
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/remotedb" "github.com/ethereum/go-ethereum/ethdb/remotedb"
"github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/ethstats"
@ -2196,7 +2195,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
if ctx.IsSet(VMTraceConfigFlag.Name) { if ctx.IsSet(VMTraceConfigFlag.Name) {
config = json.RawMessage(ctx.String(VMTraceConfigFlag.Name)) config = json.RawMessage(ctx.String(VMTraceConfigFlag.Name))
} }
t, err := live.Directory.New(name, config) t, err := tracers.LiveDirectory.New(name, config)
if err != nil { if err != nil {
Fatalf("Failed to create tracer %q: %v", name, err) Fatalf("Failed to create tracer %q: %v", name, err)
} }

View file

@ -17,11 +17,11 @@
package prque package prque
import ( import (
"cmp"
"container/heap" "container/heap"
"time" "time"
"github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/mclock"
"golang.org/x/exp/constraints"
) )
// LazyQueue is a priority queue data structure where priorities can change over // LazyQueue is a priority queue data structure where priorities can change over
@ -33,7 +33,7 @@ import (
// //
// If the upper estimate is exceeded then Update should be called for that item. // If the upper estimate is exceeded then Update should be called for that item.
// A global Refresh function should also be called periodically. // A global Refresh function should also be called periodically.
type LazyQueue[P constraints.Ordered, V any] struct { type LazyQueue[P cmp.Ordered, V any] struct {
clock mclock.Clock clock mclock.Clock
// Items are stored in one of two internal queues ordered by estimated max // Items are stored in one of two internal queues ordered by estimated max
// priority until the next and the next-after-next refresh. Update and Refresh // priority until the next and the next-after-next refresh. Update and Refresh
@ -50,12 +50,12 @@ type LazyQueue[P constraints.Ordered, V any] struct {
} }
type ( type (
PriorityCallback[P constraints.Ordered, V any] func(data V) P // actual priority callback PriorityCallback[P cmp.Ordered, V any] func(data V) P // actual priority callback
MaxPriorityCallback[P constraints.Ordered, V any] func(data V, until mclock.AbsTime) P // estimated maximum priority callback MaxPriorityCallback[P cmp.Ordered, V any] func(data V, until mclock.AbsTime) P // estimated maximum priority callback
) )
// NewLazyQueue creates a new lazy queue // NewLazyQueue creates a new lazy queue
func NewLazyQueue[P constraints.Ordered, V any](setIndex SetIndexCallback[V], priority PriorityCallback[P, V], maxPriority MaxPriorityCallback[P, V], clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue[P, V] { func NewLazyQueue[P cmp.Ordered, V any](setIndex SetIndexCallback[V], priority PriorityCallback[P, V], maxPriority MaxPriorityCallback[P, V], clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue[P, V] {
q := &LazyQueue[P, V]{ q := &LazyQueue[P, V]{
popQueue: newSstack[P, V](nil), popQueue: newSstack[P, V](nil),
setIndex: setIndex, setIndex: setIndex,

View file

@ -18,18 +18,17 @@
package prque package prque
import ( import (
"cmp"
"container/heap" "container/heap"
"golang.org/x/exp/constraints"
) )
// Priority queue data structure. // Priority queue data structure.
type Prque[P constraints.Ordered, V any] struct { type Prque[P cmp.Ordered, V any] struct {
cont *sstack[P, V] cont *sstack[P, V]
} }
// New creates a new priority queue. // New creates a new priority queue.
func New[P constraints.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] { func New[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *Prque[P, V] {
return &Prque[P, V]{newSstack[P, V](setIndex)} return &Prque[P, V]{newSstack[P, V](setIndex)}
} }

View file

@ -10,13 +10,13 @@
package prque package prque
import "golang.org/x/exp/constraints" import "cmp"
// The size of a block of data // The size of a block of data
const blockSize = 4096 const blockSize = 4096
// A prioritized item in the sorted stack. // A prioritized item in the sorted stack.
type item[P constraints.Ordered, V any] struct { type item[P cmp.Ordered, V any] struct {
value V value V
priority P priority P
} }
@ -29,7 +29,7 @@ type SetIndexCallback[V any] func(data V, index int)
// Internal sortable stack data structure. Implements the Push and Pop ops for // Internal sortable stack data structure. Implements the Push and Pop ops for
// the stack (heap) functionality and the Len, Less and Swap methods for the // the stack (heap) functionality and the Len, Less and Swap methods for the
// sortability requirements of the heaps. // sortability requirements of the heaps.
type sstack[P constraints.Ordered, V any] struct { type sstack[P cmp.Ordered, V any] struct {
setIndex SetIndexCallback[V] setIndex SetIndexCallback[V]
size int size int
capacity int capacity int
@ -40,7 +40,7 @@ type sstack[P constraints.Ordered, V any] struct {
} }
// Creates a new, empty stack. // Creates a new, empty stack.
func newSstack[P constraints.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V] { func newSstack[P cmp.Ordered, V any](setIndex SetIndexCallback[V]) *sstack[P, V] {
result := new(sstack[P, V]) result := new(sstack[P, V])
result.setIndex = setIndex result.setIndex = setIndex
result.active = make([]*item[P, V], blockSize) result.active = make([]*item[P, V], blockSize)

View file

@ -475,3 +475,14 @@ func (d *Decimal) UnmarshalJSON(input []byte) error {
return err return err
} }
} }
type PrettyBytes []byte
// TerminalString implements log.TerminalStringer, formatting a string for console
// output during logging.
func (b PrettyBytes) TerminalString() string {
if len(b) < 7 {
return fmt.Sprintf("%x", b)
}
return fmt.Sprintf("%#x...%x (%dB)", b[:3], b[len(b)-3:], len(b))
}

View file

@ -349,46 +349,46 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H
} }
// Finalize implements consensus.Engine and processes withdrawals on top. // Finalize implements consensus.Engine and processes withdrawals on top.
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, stateDB *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
if !beacon.IsPoSHeader(header) { if !beacon.IsPoSHeader(header) {
beacon.ethone.Finalize(chain, header, stateDB, txs, uncles, nil) beacon.ethone.Finalize(chain, header, state, body)
return return
} }
// Withdrawals processing. // Withdrawals processing.
for _, w := range withdrawals { for _, w := range body.Withdrawals {
// Convert amount from gwei to wei. // Convert amount from gwei to wei.
amount := new(uint256.Int).SetUint64(w.Amount) amount := new(uint256.Int).SetUint64(w.Amount)
amount = amount.Mul(amount, uint256.NewInt(params.GWei)) amount = amount.Mul(amount, uint256.NewInt(params.GWei))
stateDB.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal) state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
} }
// No block reward which is issued by consensus layer instead. // No block reward which is issued by consensus layer instead.
} }
// FinalizeAndAssemble implements consensus.Engine, setting the final state and // FinalizeAndAssemble implements consensus.Engine, setting the final state and
// assembling the block. // assembling the block.
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
if !beacon.IsPoSHeader(header) { if !beacon.IsPoSHeader(header) {
return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts, nil) return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts)
} }
shanghai := chain.Config().IsShanghai(header.Number, header.Time) shanghai := chain.Config().IsShanghai(header.Number, header.Time)
if shanghai { if shanghai {
// All blocks after Shanghai must include a withdrawals root. // All blocks after Shanghai must include a withdrawals root.
if withdrawals == nil { if body.Withdrawals == nil {
withdrawals = make([]*types.Withdrawal, 0) body.Withdrawals = make([]*types.Withdrawal, 0)
} }
} else { } else {
if len(withdrawals) > 0 { if len(body.Withdrawals) > 0 {
return nil, errors.New("withdrawals set before Shanghai activation") return nil, errors.New("withdrawals set before Shanghai activation")
} }
} }
// Finalize and assemble the block. // Finalize and assemble the block.
beacon.Finalize(chain, header, state, txs, uncles, withdrawals) beacon.Finalize(chain, header, state, body)
// Assign the final state root to header. // Assign the final state root to header.
header.Root = state.IntermediateRoot(true) header.Root = state.IntermediateRoot(true)
// Assemble and return the final block. // Assemble and return the final block.
return types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), nil return types.NewBlockWithWithdrawals(header, body.Transactions, body.Uncles, receipts, body.Withdrawals, trie.NewStackTrie(nil)), nil
} }
// Seal generates a new sealing request for the given input block and pushes // Seal generates a new sealing request for the given input block and pushes

View file

@ -580,24 +580,24 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
// Finalize implements consensus.Engine. There is no post-transaction // Finalize implements consensus.Engine. There is no post-transaction
// consensus rules in clique, do nothing here. // consensus rules in clique, do nothing here.
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
// No block rewards in PoA, so the state remains as is // No block rewards in PoA, so the state remains as is
} }
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block. // nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
if len(withdrawals) > 0 { if len(body.Withdrawals) > 0 {
return nil, errors.New("clique does not support withdrawals") return nil, errors.New("clique does not support withdrawals")
} }
// Finalize block // Finalize block
c.Finalize(chain, header, state, txs, uncles, nil) c.Finalize(chain, header, state, body)
// Assign the final state root to header. // Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Assemble and return the final block for sealing. // Assemble and return the final block for sealing.
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil return types.NewBlock(header, body.Transactions, nil, receipts, trie.NewStackTrie(nil)), nil
} }
// Authorize injects a private key into the consensus engine to mint new blocks // Authorize injects a private key into the consensus engine to mint new blocks

View file

@ -19,6 +19,7 @@ package clique
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"slices"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -28,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"golang.org/x/exp/slices"
) )
// Vote represents a single vote that an authorized signer made to modify the // Vote represents a single vote that an authorized signer made to modify the

View file

@ -21,6 +21,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"math/big" "math/big"
"slices"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -30,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"golang.org/x/exp/slices"
) )
// testerAccountPool is a pool to maintain currently active tester accounts, // testerAccountPool is a pool to maintain currently active tester accounts,

View file

@ -88,16 +88,14 @@ type Engine interface {
// //
// Note: The state database might be updated to reflect any consensus rules // Note: The state database might be updated to reflect any consensus rules
// that happen at finalization (e.g. block rewards). // that happen at finalization (e.g. block rewards).
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body)
uncles []*types.Header, withdrawals []*types.Withdrawal)
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
// rewards or process withdrawals) and assembles the final block. // rewards or process withdrawals) and assembles the final block.
// //
// Note: The block header and state database might be updated to reflect any // Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards). // consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes // Seal generates a new sealing request for the given input block and pushes
// the result into the given channel. // the result into the given channel.

View file

@ -502,25 +502,25 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
} }
// Finalize implements consensus.Engine, accumulating the block and uncle rewards. // Finalize implements consensus.Engine, accumulating the block and uncle rewards.
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
// Accumulate any block and uncle rewards // Accumulate any block and uncle rewards
accumulateRewards(chain.Config(), state, header, uncles) accumulateRewards(chain.Config(), state, header, body.Uncles)
} }
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and // FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block. // uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
if len(withdrawals) > 0 { if len(body.Withdrawals) > 0 {
return nil, errors.New("ethash does not support withdrawals") return nil, errors.New("ethash does not support withdrawals")
} }
// Finalize block // Finalize block
ethash.Finalize(chain, header, state, txs, uncles, nil) ethash.Finalize(chain, header, state, body)
// Assign the final state root to header. // Assign the final state root to header.
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil return types.NewBlock(header, body.Transactions, body.Uncles, receipts, trie.NewStackTrie(nil)), nil
} }
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.

View file

@ -66,7 +66,7 @@ func (it *instructionIterator) Next() bool {
it.op = vm.OpCode(it.code[it.pc]) it.op = vm.OpCode(it.code[it.pc])
if it.op.IsPush() { if it.op.IsPush() {
a := uint64(it.op) - uint64(vm.PUSH1) + 1 a := uint64(it.op) - uint64(vm.PUSH0)
u := it.pc + 1 + a u := it.pc + 1 + a
if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u { if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
it.error = fmt.Errorf("incomplete push instruction at %v", it.pc) it.error = fmt.Errorf("incomplete push instruction at %v", it.pc)

View file

@ -914,7 +914,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
// touching the header chain altogether, unless the freezer is broken // touching the header chain altogether, unless the freezer is broken
if repair { if repair {
if target, force := updateFn(bc.db, bc.CurrentBlock()); force { if target, force := updateFn(bc.db, bc.CurrentBlock()); force {
bc.hc.SetHead(target.Number.Uint64(), updateFn, delFn) bc.hc.SetHead(target.Number.Uint64(), nil, delFn)
} }
} else { } else {
// Rewind the chain to the requested head and keep going backwards until a // Rewind the chain to the requested head and keep going backwards until a
@ -1845,6 +1845,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps) throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps)
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) { go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
// Disable tracing for prefetcher executions.
vmCfg := bc.vmConfig vmCfg := bc.vmConfig
vmCfg.Tracer = nil vmCfg.Tracer = nil
bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt) bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt)

View file

@ -345,7 +345,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
gen(i, b) gen(i, b)
} }
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, b.txs, b.uncles, b.receipts, b.withdrawals) body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -47,8 +47,8 @@ func TestGeneratePOSChain(t *testing.T) {
gspec = &Genesis{ gspec = &Genesis{
Config: &config, Config: &config,
Alloc: types.GenesisAlloc{ Alloc: types.GenesisAlloc{
address: {Balance: funds}, address: {Balance: funds},
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: asm4788}, params.BeaconRootsAddress: {Balance: common.Big0, Code: asm4788},
}, },
BaseFee: big.NewInt(params.InitialBaseFee), BaseFee: big.NewInt(params.InitialBaseFee),
Difficulty: common.Big1, Difficulty: common.Big1,
@ -180,7 +180,7 @@ func TestGeneratePOSChain(t *testing.T) {
} }
state, _ := blockchain.State() state, _ := blockchain.State()
idx := block.Time()%8191 + 8191 idx := block.Time()%8191 + 8191
got := state.GetState(params.BeaconRootsStorageAddress, common.BigToHash(new(big.Int).SetUint64(idx))) got := state.GetState(params.BeaconRootsAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
if got != want { if got != want {
t.Fatalf("block %d, wrong parent beacon root in state: got %s, want %s", i, got, want) t.Fatalf("block %d, wrong parent beacon root in state: got %s, want %s", i, got, want)
} }

View file

@ -24,12 +24,12 @@ import (
"math" "math"
"math/big" "math/big"
"reflect" "reflect"
"slices"
"strings" "strings"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"golang.org/x/exp/slices"
) )
var ( var (

View file

@ -30,12 +30,12 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"os" "os"
"slices"
"strconv" "strconv"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/exp/slices"
) )
type allocItem struct { type allocItem struct {

View file

@ -22,6 +22,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"slices"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
@ -31,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/exp/slices"
) )
// ReadCanonicalHash retrieves the hash assigned to a canonical block number. // ReadCanonicalHash retrieves the hash assigned to a canonical block number.

View file

@ -276,7 +276,7 @@ func TestFreezerReadonlyValidate(t *testing.T) {
} }
require.NoError(t, f.Close()) require.NoError(t, f.Close())
// Re-openening as readonly should fail when validating // Re-opening as readonly should fail when validating
// table lengths. // table lengths.
_, err = NewFreezer(dir, "", true, 2049, tables) _, err = NewFreezer(dir, "", true, 2049, tables)
if err == nil { if err == nil {

View file

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"math" "math"
"math/rand" "math/rand"
"slices"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -29,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
bloomfilter "github.com/holiman/bloomfilter/v2" bloomfilter "github.com/holiman/bloomfilter/v2"
"golang.org/x/exp/slices"
) )
var ( var (

View file

@ -19,10 +19,10 @@ package snapshot
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"slices"
"sort" "sort"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"golang.org/x/exp/slices"
) )
// weightedIterator is a iterator with an assigned weight. It is used to prioritise // weightedIterator is a iterator with an assigned weight. It is used to prioritise

View file

@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -102,7 +101,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return nil, nil, 0, errors.New("withdrawals before shanghai") return nil, nil, 0, errors.New("withdrawals before shanghai")
} }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals) p.engine.Finalize(p.bc, header, statedb, block.Body())
return receipts, allLogs, *usedGas, nil return receipts, allLogs, *usedGas, nil
} }
@ -112,14 +111,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// this method takes an already created EVM instance as input. // this method takes an already created EVM instance as input.
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
evm.Config.Tracer.OnTxStart(&tracing.VMContext{ evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
ChainConfig: evm.ChainConfig(),
StateDB: statedb,
BlockNumber: evm.Context.BlockNumber,
Time: evm.Context.Time,
Coinbase: evm.Context.Coinbase,
Random: evm.Context.Random,
}, tx, msg.From)
if evm.Config.Tracer.OnTxEnd != nil { if evm.Config.Tracer.OnTxEnd != nil {
defer func() { defer func() {
evm.Config.Tracer.OnTxEnd(receipt, err) evm.Config.Tracer.OnTxEnd(receipt, err)
@ -202,11 +194,11 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
GasPrice: common.Big0, GasPrice: common.Big0,
GasFeeCap: common.Big0, GasFeeCap: common.Big0,
GasTipCap: common.Big0, GasTipCap: common.Big0,
To: &params.BeaconRootsStorageAddress, To: &params.BeaconRootsAddress,
Data: beaconRoot[:], Data: beaconRoot[:],
} }
vmenv.Reset(NewEVMTxContext(msg), statedb) vmenv.Reset(NewEVMTxContext(msg), statedb)
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress) statedb.AddAddressToAccessList(params.BeaconRootsAddress)
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true) statedb.Finalise(true)
} }

View file

@ -23,8 +23,8 @@ import (
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
// log2_1_125 is used in the eviction priority calculation. // log1_125 is used in the eviction priority calculation.
var log2_1_125 = math.Log2(1.125) var log1_125 = math.Log(1.125)
// evictionPriority calculates the eviction priority based on the algorithm // evictionPriority calculates the eviction priority based on the algorithm
// described in the BlobPool docs for both fee components. // described in the BlobPool docs for both fee components.
@ -57,8 +57,8 @@ func evictionPriority1D(basefeeJumps float64, txfeeJumps float64) int {
// dynamicFeeJumps calculates the log1.125(fee), namely the number of fee jumps // dynamicFeeJumps calculates the log1.125(fee), namely the number of fee jumps
// needed to reach the requested one. We only use it when calculating the jumps // needed to reach the requested one. We only use it when calculating the jumps
// between 2 fees, so it doesn't matter from what exact number with returns. // between 2 fees, so it doesn't matter from what exact number it returns.
// it returns the result from (0, 1, 1.125). // It returns the result from (0, 1, 1.125).
// //
// This method is very expensive, taking about 75ns on a very recent laptop CPU, // This method is very expensive, taking about 75ns on a very recent laptop CPU,
// but the result does not change with the lifetime of a transaction, so it can // but the result does not change with the lifetime of a transaction, so it can
@ -67,7 +67,7 @@ func dynamicFeeJumps(fee *uint256.Int) float64 {
if fee.IsZero() { if fee.IsZero() {
return 0 // can't log2 zero, should never happen outside tests, but don't choke return 0 // can't log2 zero, should never happen outside tests, but don't choke
} }
return math.Log2(fee.Float64()) / log2_1_125 return math.Log(fee.Float64()) / log1_125
} }
// intLog2 is a helper to calculate the integral part of a log2 of an unsigned // intLog2 is a helper to calculate the integral part of a log2 of an unsigned

View file

@ -20,6 +20,7 @@ import (
"container/heap" "container/heap"
"math" "math"
"math/big" "math/big"
"slices"
"sort" "sort"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -28,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/exp/slices"
) )
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for

View file

@ -33,7 +33,7 @@ import (
var ( var (
// blobTxMinBlobGasPrice is the big.Int version of the configured protocol // blobTxMinBlobGasPrice is the big.Int version of the configured protocol
// parameter to avoid constucting a new big integer for every transaction. // parameter to avoid constructing a new big integer for every transaction.
blobTxMinBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice) blobTxMinBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice)
) )

View file

@ -93,9 +93,7 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
bits := make(bitvec, len(code)/8+1+4) bits := make(bitvec, len(code)/8+1+4)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
for j := range bits { clear(bits)
bits[j] = 0
}
codeBitmapInternal(code, bits) codeBitmapInternal(code, bits)
} }
} }

View file

@ -123,7 +123,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
sender = vm.AccountRef(cfg.Origin) sender = vm.AccountRef(cfg.Origin)
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
) )
if cfg.EVMConfig.Tracer != nil { if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
} }
// Execute the preparatory steps for state transition which includes: // Execute the preparatory steps for state transition which includes:
@ -159,7 +159,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
sender = vm.AccountRef(cfg.Origin) sender = vm.AccountRef(cfg.Origin)
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
) )
if cfg.EVMConfig.Tracer != nil { if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
} }
// Execute the preparatory steps for state transition which includes: // Execute the preparatory steps for state transition which includes:
@ -190,7 +190,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
statedb = cfg.State statedb = cfg.State
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
) )
if cfg.EVMConfig.Tracer != nil { if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
} }
// Execute the preparatory steps for state transition which includes: // Execute the preparatory steps for state transition which includes:

View file

@ -32,7 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -331,7 +331,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
cfg.GasLimit = gas cfg.GasLimit = gas
if len(tracerCode) > 0 { if len(tracerCode) > 0 {
tracer, err := directory.DefaultDirectory.New(tracerCode, new(directory.Context), nil) tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -827,7 +827,7 @@ func TestRuntimeJSTracer(t *testing.T) {
statedb.SetCode(common.HexToAddress("0xee"), calleeCode) statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
statedb.SetCode(common.HexToAddress("0xff"), suicideCode) statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil) tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -862,7 +862,7 @@ func TestJSTracerCreateTx(t *testing.T) {
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)} code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil) tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -29,7 +29,7 @@ import (
) )
// BUG(agl): this implementation is not constant time. // BUG(agl): this implementation is not constant time.
// TODO(agl): keep GF(p²) elements in Mongomery form. // TODO(agl): keep GF(p²) elements in Montgomery form.
// G1 is an abstract cyclic group. The zero value is suitable for use as the // G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input. // output of an operation, but cannot be used as an input.

View file

@ -51,6 +51,15 @@ var (
var errInvalidPubkey = errors.New("invalid secp256k1 public key") var errInvalidPubkey = errors.New("invalid secp256k1 public key")
// EllipticCurve contains curve operations.
type EllipticCurve interface {
elliptic.Curve
// Point marshaling/unmarshaing.
Marshal(x, y *big.Int) []byte
Unmarshal(data []byte) (x, y *big.Int)
}
// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports // KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum // Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state. // because it doesn't copy the internal state, but also modifies the internal state.
@ -148,7 +157,7 @@ func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
return nil, errors.New("invalid private key, zero or negative") return nil, errors.New("invalid private key, zero or negative")
} }
priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d) priv.PublicKey.X, priv.PublicKey.Y = S256().ScalarBaseMult(d)
if priv.PublicKey.X == nil { if priv.PublicKey.X == nil {
return nil, errors.New("invalid private key") return nil, errors.New("invalid private key")
} }
@ -165,7 +174,7 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
// UnmarshalPubkey converts bytes to a secp256k1 public key. // UnmarshalPubkey converts bytes to a secp256k1 public key.
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) { func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
x, y := elliptic.Unmarshal(S256(), pub) x, y := S256().Unmarshal(pub)
if x == nil { if x == nil {
return nil, errInvalidPubkey return nil, errInvalidPubkey
} }
@ -176,7 +185,7 @@ func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil { if pub == nil || pub.X == nil || pub.Y == nil {
return nil return nil
} }
return elliptic.Marshal(S256(), pub.X, pub.Y) return S256().Marshal(pub.X, pub.Y)
} }
// HexToECDSA parses a secp256k1 private key. // HexToECDSA parses a secp256k1 private key.
@ -278,7 +287,5 @@ func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
} }
func zeroBytes(bytes []byte) { func zeroBytes(bytes []byte) {
for i := range bytes { clear(bytes)
bytes[i] = 0
}
} }

View file

@ -40,6 +40,8 @@ import (
"hash" "hash"
"io" "io"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto"
) )
var ( var (
@ -95,15 +97,15 @@ func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
// Generate an elliptic curve public / private keypair. If params is nil, // Generate an elliptic curve public / private keypair. If params is nil,
// the recommended default parameters for the key will be chosen. // the recommended default parameters for the key will be chosen.
func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) { func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
pb, x, y, err := elliptic.GenerateKey(curve, rand) sk, err := ecdsa.GenerateKey(curve, rand)
if err != nil { if err != nil {
return return
} }
prv = new(PrivateKey) prv = new(PrivateKey)
prv.PublicKey.X = x prv.PublicKey.X = sk.X
prv.PublicKey.Y = y prv.PublicKey.Y = sk.Y
prv.PublicKey.Curve = curve prv.PublicKey.Curve = curve
prv.D = new(big.Int).SetBytes(pb) prv.D = new(big.Int).Set(sk.D)
if params == nil { if params == nil {
params = ParamsFromCurve(curve) params = ParamsFromCurve(curve)
} }
@ -255,12 +257,15 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e
d := messageTag(params.Hash, Km, em, s2) d := messageTag(params.Hash, Km, em, s2)
Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y) if curve, ok := pub.Curve.(crypto.EllipticCurve); ok {
ct = make([]byte, len(Rb)+len(em)+len(d)) Rb := curve.Marshal(R.PublicKey.X, R.PublicKey.Y)
copy(ct, Rb) ct = make([]byte, len(Rb)+len(em)+len(d))
copy(ct[len(Rb):], em) copy(ct, Rb)
copy(ct[len(Rb)+len(em):], d) copy(ct[len(Rb):], em)
return ct, nil copy(ct[len(Rb)+len(em):], d)
return ct, nil
}
return nil, ErrInvalidCurve
} }
// Decrypt decrypts an ECIES ciphertext. // Decrypt decrypts an ECIES ciphertext.
@ -297,21 +302,24 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
R := new(PublicKey) R := new(PublicKey)
R.Curve = prv.PublicKey.Curve R.Curve = prv.PublicKey.Curve
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
if R.X == nil {
return nil, ErrInvalidPublicKey
}
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) if curve, ok := R.Curve.(crypto.EllipticCurve); ok {
if err != nil { R.X, R.Y = curve.Unmarshal(c[:rLen])
return nil, err if R.X == nil {
} return nil, ErrInvalidPublicKey
Ke, Km := deriveKeys(hash, z, s1, params.KeyLen) }
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { if err != nil {
return nil, ErrInvalidMessage return nil, err
} }
Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
return symDecrypt(params, Ke, c[mStart:mEnd]) d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
return nil, ErrInvalidMessage
}
return symDecrypt(params, Ke, c[mStart:mEnd])
}
return nil, ErrInvalidCurve
} }

View file

@ -44,12 +44,8 @@ func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int,
// Unpack the result and clear temporaries. // Unpack the result and clear temporaries.
x := new(big.Int).SetBytes(point[:32]) x := new(big.Int).SetBytes(point[:32])
y := new(big.Int).SetBytes(point[32:]) y := new(big.Int).SetBytes(point[32:])
for i := range point { clear(point)
point[i] = 0 clear(scalar)
}
for i := range padded {
scalar[i] = 0
}
if res != 1 { if res != 1 {
return nil, nil return nil, nil
} }

View file

@ -10,7 +10,6 @@ package secp256k1
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"io" "io"
@ -24,7 +23,7 @@ func generateKeyPair() (pubkey, privkey []byte) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
pubkey = elliptic.Marshal(S256(), key.X, key.Y) pubkey = S256().Marshal(key.X, key.Y)
privkey = make([]byte, 32) privkey = make([]byte, 32)
blob := key.D.Bytes() blob := key.D.Bytes()

View file

@ -21,7 +21,6 @@ package crypto
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
@ -40,9 +39,7 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return UnmarshalPubkey(s)
x, y := elliptic.Unmarshal(S256(), s)
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
} }
// Sign calculates an ECDSA signature. // Sign calculates an ECDSA signature.
@ -84,6 +81,6 @@ func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
} }
// S256 returns an instance of the secp256k1 curve. // S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve { func S256() EllipticCurve {
return secp256k1.S256() return secp256k1.S256()
} }

View file

@ -21,9 +21,9 @@ package crypto
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
"math/big"
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
@ -58,7 +58,13 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return pub.ToECDSA(), nil // We need to explicitly set the curve here, because we're wrapping
// the original curve to add (un-)marshalling
return &ecdsa.PublicKey{
Curve: S256(),
X: pub.X(),
Y: pub.Y(),
}, nil
} }
// Sign calculates an ECDSA signature. // Sign calculates an ECDSA signature.
@ -73,7 +79,7 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
if len(hash) != 32 { if len(hash) != 32 {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
} }
if prv.Curve != btcec.S256() { if prv.Curve != S256() {
return nil, errors.New("private key curve is not secp256k1") return nil, errors.New("private key curve is not secp256k1")
} }
// ecdsa.PrivateKey -> btcec.PrivateKey // ecdsa.PrivateKey -> btcec.PrivateKey
@ -128,7 +134,13 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return key.ToECDSA(), nil // We need to explicitly set the curve here, because we're wrapping
// the original curve to add (un-)marshalling
return &ecdsa.PublicKey{
Curve: S256(),
X: key.X(),
Y: key.Y(),
}, nil
} }
// CompressPubkey encodes a public key to the 33-byte compressed format. The // CompressPubkey encodes a public key to the 33-byte compressed format. The
@ -147,6 +159,38 @@ func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
} }
// S256 returns an instance of the secp256k1 curve. // S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve { func S256() EllipticCurve {
return btcec.S256() return btCurve{btcec.S256()}
}
type btCurve struct {
*btcec.KoblitzCurve
}
// Marshall converts a point given as (x, y) into a byte slice.
func (curve btCurve) Marshal(x, y *big.Int) []byte {
byteLen := (curve.Params().BitSize + 7) / 8
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point
x.FillBytes(ret[1 : 1+byteLen])
y.FillBytes(ret[1+byteLen : 1+2*byteLen])
return ret
}
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
// error, x = nil.
func (curve btCurve) Unmarshal(data []byte) (x, y *big.Int) {
byteLen := (curve.Params().BitSize + 7) / 8
if len(data) != 1+2*byteLen {
return nil, nil
}
if data[0] != 4 { // uncompressed form
return nil, nil
}
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
y = new(big.Int).SetBytes(data[1+byteLen:])
return
} }

View file

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"reflect" "reflect"
"slices"
"strings" "strings"
"testing" "testing"
@ -32,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/exp/slices"
) )
var dumper = spew.ConfigState{Indent: " "} var dumper = spew.ConfigState{Indent: " "}

View file

@ -43,7 +43,7 @@ import (
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
@ -101,8 +101,8 @@ type Ethereum struct {
shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
} }
// New creates a new Ethereum object (including the // New creates a new Ethereum object (including the initialisation of the common Ethereum object),
// initialisation of the common Ethereum object) // whose lifecycle will be managed by the provided node.
func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
// Ensure configuration values are compatible and sane // Ensure configuration values are compatible and sane
if config.SyncMode == downloader.LightSync { if config.SyncMode == downloader.LightSync {
@ -206,7 +206,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if config.VMTraceConfig != "" { if config.VMTraceConfig != "" {
traceConfig = json.RawMessage(config.VMTraceConfig) traceConfig = json.RawMessage(config.VMTraceConfig)
} }
t, err := live.Directory.New(config.VMTrace, traceConfig) t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to create tracer %s: %v", config.VMTrace, err) return nil, fmt.Errorf("Failed to create tracer %s: %v", config.VMTrace, err)
} }

View file

@ -20,6 +20,7 @@ package catalyst
import ( import (
"errors" "errors"
"fmt" "fmt"
"strconv"
"sync" "sync"
"time" "time"
@ -540,7 +541,33 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot) block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot)
if err != nil { if err != nil {
log.Warn("Invalid NewPayload params", "params", params, "error", err) bgu := "nil"
if params.BlobGasUsed != nil {
bgu = strconv.Itoa(int(*params.BlobGasUsed))
}
ebg := "nil"
if params.BlobGasUsed != nil {
ebg = strconv.Itoa(int(*params.ExcessBlobGas))
}
log.Warn("Invalid NewPayload params",
"params.Number", params.Number,
"params.ParentHash", params.ParentHash,
"params.BlockHash", params.BlockHash,
"params.StateRoot", params.StateRoot,
"params.FeeRecipient", params.FeeRecipient,
"params.LogsBloom", common.PrettyBytes(params.LogsBloom),
"params.Random", params.Random,
"params.GasLimit", params.GasLimit,
"params.GasUsed", params.GasUsed,
"params.Timestamp", params.Timestamp,
"params.ExtraData", common.PrettyBytes(params.ExtraData),
"params.BaseFeePerGas", params.BaseFeePerGas,
"params.BlobGasUsed", bgu,
"params.ExcessBlobGas", ebg,
"len(params.Transactions)", len(params.Transactions),
"len(params.Withdrawals)", len(params.Withdrawals),
"beaconRoot", beaconRoot,
"error", err)
return api.invalid(err, nil), nil return api.invalid(err, nil), nil
} }
// Stash away the last update to warn the user if the beacon client goes offline // Stash away the last update to warn the user if the beacon client goes offline
@ -567,7 +594,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
// update after legit payload executions. // update after legit payload executions.
parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1) parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil { if parent == nil {
return api.delayPayloadImport(block) return api.delayPayloadImport(block), nil
} }
// We have an existing parent, do some sanity checks to avoid the beacon client // We have an existing parent, do some sanity checks to avoid the beacon client
// triggering too early // triggering too early
@ -593,7 +620,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
// into the database directly will conflict with the assumptions of snap sync // into the database directly will conflict with the assumptions of snap sync
// that it has an empty db that it can fill itself. // that it has an empty db that it can fill itself.
if api.eth.SyncMode() != downloader.FullSync { if api.eth.SyncMode() != downloader.FullSync {
return api.delayPayloadImport(block) return api.delayPayloadImport(block), nil
} }
if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
api.remoteBlocks.put(block.Hash(), block.Header()) api.remoteBlocks.put(block.Hash(), block.Header())
@ -619,11 +646,11 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
// either via a forkchoice update or a sync extension. This method is meant to // either via a forkchoice update or a sync extension. This method is meant to
// be called by the newpayload command when the block seems to be ok, but some // be called by the newpayload command when the block seems to be ok, but some
// prerequisite prevents it from being processed (e.g. no parent, or snap sync). // prerequisite prevents it from being processed (e.g. no parent, or snap sync).
func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) delayPayloadImport(block *types.Block) engine.PayloadStatusV1 {
// Sanity check that this block's parent is not on a previously invalidated // Sanity check that this block's parent is not on a previously invalidated
// chain. If it is, mark the block as invalid too. // chain. If it is, mark the block as invalid too.
if res := api.checkInvalidAncestor(block.ParentHash(), block.Hash()); res != nil { if res := api.checkInvalidAncestor(block.ParentHash(), block.Hash()); res != nil {
return *res, nil return *res
} }
// Stash the block away for a potential forced forkchoice update to it // Stash the block away for a potential forced forkchoice update to it
// at a later time. // at a later time.
@ -635,7 +662,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadS
err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header()) err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header())
if err == nil { if err == nil {
log.Debug("Payload accepted for sync extension", "number", block.NumberU64(), "hash", block.Hash()) log.Debug("Payload accepted for sync extension", "number", block.NumberU64(), "hash", block.Hash())
return engine.PayloadStatusV1{Status: engine.SYNCING}, nil return engine.PayloadStatusV1{Status: engine.SYNCING}
} }
// Either no beacon sync was started yet, or it rejected the delivered // Either no beacon sync was started yet, or it rejected the delivered
// payload as non-integratable on top of the existing sync. We'll just // payload as non-integratable on top of the existing sync. We'll just
@ -652,7 +679,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadS
// and cannot afford concurrent out-if-band modifications via imports. // and cannot afford concurrent out-if-band modifications via imports.
log.Warn("Ignoring payload while snap syncing", "number", block.NumberU64(), "hash", block.Hash(), "reason", err) log.Warn("Ignoring payload while snap syncing", "number", block.NumberU64(), "hash", block.Hash(), "reason", err)
} }
return engine.PayloadStatusV1{Status: engine.SYNCING}, nil return engine.PayloadStatusV1{Status: engine.SYNCING}
} }
// setInvalidAncestor is a callback for the downloader to notify us if a bad block // setInvalidAncestor is a callback for the downloader to notify us if a bad block

View file

@ -72,8 +72,8 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
genesis := &core.Genesis{ genesis := &core.Genesis{
Config: &config, Config: &config,
Alloc: types.GenesisAlloc{ Alloc: types.GenesisAlloc{
testAddr: {Balance: testBalance}, testAddr: {Balance: testBalance},
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500")}, params.BeaconRootsAddress: {Balance: common.Big0, Code: common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500")},
}, },
ExtraData: []byte("test genesis"), ExtraData: []byte("test genesis"),
Timestamp: 9000, Timestamp: 9000,
@ -1650,10 +1650,10 @@ func TestParentBeaconBlockRoot(t *testing.T) {
rootIdx = common.BigToHash(big.NewInt(int64((execData.ExecutionPayload.Timestamp % 98304) + 98304))) rootIdx = common.BigToHash(big.NewInt(int64((execData.ExecutionPayload.Timestamp % 98304) + 98304)))
) )
if num := db.GetState(params.BeaconRootsStorageAddress, timeIdx); num != timeIdx { if num := db.GetState(params.BeaconRootsAddress, timeIdx); num != timeIdx {
t.Fatalf("incorrect number stored: want %s, got %s", timeIdx, num) t.Fatalf("incorrect number stored: want %s, got %s", timeIdx, num)
} }
if root := db.GetState(params.BeaconRootsStorageAddress, rootIdx); root != *blockParams.BeaconRoot { if root := db.GetState(params.BeaconRootsAddress, rootIdx); root != *blockParams.BeaconRoot {
t.Fatalf("incorrect root stored: want %s, got %s", *blockParams.BeaconRoot, root) t.Fatalf("incorrect root stored: want %s, got %s", *blockParams.BeaconRoot, root)
} }
} }

View file

@ -1,88 +0,0 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it 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 go-ethereum library is distributed in the hope that it 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 <http://www.gnu.org/licenses/>.
package catalyst
import (
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/beacon/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
)
// Blsync tracks the head of the beacon chain through the beacon light client
// and drives the local node via ConsensusAPI.
type Blsync struct {
engine *ConsensusAPI
client Client
headCh chan types.ChainHeadEvent
headSub event.Subscription
quitCh chan struct{}
}
type Client interface {
SubscribeChainHeadEvent(ch chan<- types.ChainHeadEvent) event.Subscription
Start()
Stop()
}
// NewBlsync creates a new beacon light syncer.
func NewBlsync(client Client, eth *eth.Ethereum) *Blsync {
return &Blsync{
engine: newConsensusAPIWithoutHeartbeat(eth),
client: client,
headCh: make(chan types.ChainHeadEvent, 16),
quitCh: make(chan struct{}),
}
}
// Start starts underlying beacon light client and the sync logic for driving
// the local node.
func (b *Blsync) Start() error {
log.Info("Beacon light sync started")
b.headSub = b.client.SubscribeChainHeadEvent(b.headCh)
go b.client.Start()
for {
select {
case <-b.quitCh:
return nil
case head := <-b.headCh:
if _, err := b.engine.NewPayloadV2(*head.HeadBlock); err != nil {
log.Error("failed to send new payload", "err", err)
continue
}
update := engine.ForkchoiceStateV1{
HeadBlockHash: head.HeadBlock.BlockHash,
SafeBlockHash: head.Finalized, //TODO pass finalized or empty hash here?
FinalizedBlockHash: head.Finalized,
}
if _, err := b.engine.ForkchoiceUpdatedV1(update, nil); err != nil {
log.Error("failed to send forkchoice updated", "err", err)
continue
}
}
}
}
// Stop signals to the light client and syncer to exit.
func (b *Blsync) Stop() error {
b.client.Stop()
close(b.quitCh)
return nil
}

View file

@ -39,10 +39,10 @@ import (
) )
var ( var (
MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request MaxBlockFetch = 128 // Number of blocks to be fetched per retrieval request
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request MaxHeaderFetch = 192 // Number of block headers to be fetched per retrieval request
MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly MaxSkeletonSize = 128 // Number of header fetches needed for a skeleton assembly
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request MaxReceiptFetch = 256 // Number of transaction receipts to allow fetching per request
maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection) maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain maxHeadersProcess = 2048 // Number of header download results to import at once into the chain

View file

@ -18,6 +18,7 @@ package downloader
import ( import (
"fmt" "fmt"
"log/slog"
"math/big" "math/big"
"math/rand" "math/rand"
"os" "os"
@ -32,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"golang.org/x/exp/slog"
) )
// makeChain creates a chain of n blocks starting at and including parent. // makeChain creates a chain of n blocks starting at and including parent.

View file

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"slices"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -30,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/exp/slices"
) )
var ( var (

View file

@ -19,6 +19,7 @@ package gasprice
import ( import (
"context" "context"
"math/big" "math/big"
"slices"
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -30,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/exp/slices"
) )
const sampleNumber = 3 // Number of transactions sampled in a block const sampleNumber = 3 // Number of transactions sampled in a block

View file

@ -390,7 +390,7 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
} }
// Interconnect all the sink handlers with the source handler // Interconnect all the sink handlers with the source handler
for i, sink := range sinks { for i, sink := range sinks {
sink := sink // Closure for gorotuine below sink := sink // Closure for goroutine below
sourcePipe, sinkPipe := p2p.MsgPipe() sourcePipe, sinkPipe := p2p.MsgPipe()
defer sourcePipe.Close() defer sourcePipe.Close()

View file

@ -42,14 +42,6 @@ const (
maxQueuedTxAnns = 4096 maxQueuedTxAnns = 4096
) )
// max is a helper function which returns the larger of the two given integers.
func max(a, b int) int {
if a > b {
return a
}
return b
}
// Peer is a collection of relevant information we have about a `eth` peer. // Peer is a collection of relevant information we have about a `eth` peer.
type Peer struct { type Peer struct {
id string // Unique ID for the peer, cached id string // Unique ID for the peer, cached

View file

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
mrand "math/rand" mrand "math/rand"
"slices"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -32,16 +33,15 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/testutil"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"golang.org/x/exp/slices"
) )
func TestHashing(t *testing.T) { func TestHashing(t *testing.T) {
@ -1816,8 +1816,8 @@ func makeUnevenStorageTrie(owner common.Hash, slots int, db *triedb.Database) (c
break break
} }
for j := 0; j < slots/3; j++ { for j := 0; j < slots/3; j++ {
key := append([]byte{byte(n)}, testutil.RandBytes(31)...) key := append([]byte{byte(n)}, testrand.Bytes(31)...)
val, _ := rlp.EncodeToBytes(testutil.RandBytes(32)) val, _ := rlp.EncodeToBytes(testrand.Bytes(32))
elem := &kv{key, val} elem := &kv{key, val}
tr.MustUpdate(elem.k, elem.v) tr.MustUpdate(elem.k, elem.v)
@ -1873,8 +1873,9 @@ func verifyTrie(scheme string, db ethdb.KeyValueStore, root common.Hash, t *test
// TestSyncAccountPerformance tests how efficient the snap algo is at minimizing // TestSyncAccountPerformance tests how efficient the snap algo is at minimizing
// state healing // state healing
func TestSyncAccountPerformance(t *testing.T) { func TestSyncAccountPerformance(t *testing.T) {
t.Parallel() // These tests must not run in parallel: they modify the
// global var accountConcurrency
// t.Parallel()
testSyncAccountPerformance(t, rawdb.HashScheme) testSyncAccountPerformance(t, rawdb.HashScheme)
testSyncAccountPerformance(t, rawdb.PathScheme) testSyncAccountPerformance(t, rawdb.PathScheme)
} }

View file

@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
@ -273,7 +272,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
// Trace all the transactions contained within // Trace all the transactions contained within
for i, tx := range task.block.Transactions() { for i, tx := range task.block.Transactions() {
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee())
txctx := &directory.Context{ txctx := &Context{
BlockHash: task.block.Hash(), BlockHash: task.block.Hash(),
BlockNumber: task.block.Number(), BlockNumber: task.block.Number(),
TxIndex: i, TxIndex: i,
@ -590,7 +589,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// process that generates states in one thread and traces txes // process that generates states in one thread and traces txes
// in separate worker threads. // in separate worker threads.
if config != nil && config.Tracer != nil && *config.Tracer != "" { if config != nil && config.Tracer != nil && *config.Tracer != "" {
if isJS := directory.DefaultDirectory.IsJS(*config.Tracer); isJS { if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS {
return api.traceBlockParallel(ctx, block, statedb, config) return api.traceBlockParallel(ctx, block, statedb, config)
} }
} }
@ -605,7 +604,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
for i, tx := range txs { for i, tx := range txs {
// Generate the next state snapshot fast without tracing // Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
txctx := &directory.Context{ txctx := &Context{
BlockHash: blockHash, BlockHash: blockHash,
BlockNumber: block.Number(), BlockNumber: block.Number(),
TxIndex: i, TxIndex: i,
@ -644,7 +643,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// Fetch and execute the next transaction trace tasks // Fetch and execute the next transaction trace tasks
for task := range jobs { for task := range jobs {
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee()) msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee())
txctx := &directory.Context{ txctx := &Context{
BlockHash: blockHash, BlockHash: blockHash,
BlockNumber: block.Number(), BlockNumber: block.Number(),
TxIndex: task.index, TxIndex: task.index,
@ -859,7 +858,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
return nil, err return nil, err
} }
txctx := &directory.Context{ txctx := &Context{
BlockHash: blockHash, BlockHash: blockHash,
BlockNumber: block.Number(), BlockNumber: block.Number(),
TxIndex: int(index), TxIndex: int(index),
@ -926,27 +925,26 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
config.BlockOverrides.Apply(&vmctx) config.BlockOverrides.Apply(&vmctx)
} }
// Execute the trace // Execute the trace
msg, err := args.ToMessage(api.backend.RPCGasCap(), vmctx.BaseFee) if err := args.CallDefaults(api.backend.RPCGasCap(), vmctx.BaseFee, api.backend.ChainConfig().ChainID); err != nil {
if err != nil {
return nil, err return nil, err
} }
tx, err := argsToTransaction(api.backend, &args, msg) var (
if err != nil { msg = args.ToMessage(vmctx.BaseFee)
return nil, err tx = args.ToTransaction()
} traceConfig *TraceConfig
var traceConfig *TraceConfig )
if config != nil { if config != nil {
traceConfig = &config.TraceConfig traceConfig = &config.TraceConfig
} }
return api.traceTx(ctx, tx, msg, new(directory.Context), vmctx, statedb, traceConfig) return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig)
} }
// traceTx configures a new tracer according to the provided configuration, and // traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will // executes the given message in the provided environment. The return value will
// be tracer dependent. // be tracer dependent.
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
var ( var (
tracer *directory.Tracer tracer *Tracer
err error err error
timeout = defaultTraceTimeout timeout = defaultTraceTimeout
usedGas uint64 usedGas uint64
@ -955,9 +953,15 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
config = &TraceConfig{} config = &TraceConfig{}
} }
// Default tracer is the struct logger // Default tracer is the struct logger
tracer = logger.NewStructLogger(config.Config).Tracer() if config.Tracer == nil {
if config.Tracer != nil { logger := logger.NewStructLogger(config.Config)
tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) tracer = &Tracer{
Hooks: logger.Hooks(),
GetResult: logger.GetResult,
Stop: logger.Stop,
}
} else {
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1050,54 +1054,3 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig)
return copy, canon return copy, canon
} }
// argsToTransaction produces a Transaction object given call arguments.
// The `msg` field must be converted from the same arguments.
func argsToTransaction(b Backend, args *ethapi.TransactionArgs, msg *core.Message) (*types.Transaction, error) {
chainID := b.ChainConfig().ChainID
if args.ChainID != nil {
if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 {
return nil, fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID)
}
}
var data types.TxData
switch {
case args.MaxFeePerGas != nil:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.DynamicFeeTx{
To: args.To,
ChainID: chainID,
Nonce: msg.Nonce,
Gas: msg.GasLimit,
GasFeeCap: msg.GasFeeCap,
GasTipCap: msg.GasTipCap,
Value: msg.Value,
Data: msg.Data,
AccessList: al,
}
case args.AccessList != nil:
data = &types.AccessListTx{
To: args.To,
ChainID: chainID,
Nonce: msg.Nonce,
Gas: msg.GasLimit,
GasPrice: msg.GasPrice,
Value: msg.Value,
Data: msg.Data,
AccessList: *args.AccessList,
}
default:
data = &types.LegacyTx{
To: args.To,
Nonce: msg.Nonce,
Gas: msg.GasLimit,
GasPrice: msg.GasPrice,
Value: msg.Value,
Data: msg.Data,
}
}
return types.NewTx(data), nil
}

View file

@ -24,6 +24,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"reflect" "reflect"
"slices"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@ -43,7 +44,6 @@ import (
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/exp/slices"
) )
var ( var (

View file

@ -14,10 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package directory provides functionality to lookup tracers by name. package tracers
// It also includes utility functions that are imported by the other
// tracing packages.
package directory
import ( import (
"encoding/json" "encoding/json"

View file

@ -24,7 +24,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2" pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -45,7 +44,7 @@ var emptyCommonHash = common.Hash{}
func init() { func init() {
staticFirehoseChainValidationOnInit() staticFirehoseChainValidationOnInit()
live.Directory.Register("firehose", newFirehoseTracer) LiveDirectory.Register("firehose", newFirehoseTracer)
} }
func newFirehoseTracer(cfg json.RawMessage) (*tracing.Hooks, error) { func newFirehoseTracer(cfg json.RawMessage) (*tracing.Hooks, error) {

View file

@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
@ -139,7 +139,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
) )
state.Close() state.Close()
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
@ -247,7 +247,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), nil) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil)
if err != nil { if err != nil {
b.Fatalf("failed to create call tracer: %v", err) b.Fatalf("failed to create call tracer: %v", err)
} }
@ -283,8 +283,8 @@ func TestInternals(t *testing.T) {
BaseFee: new(big.Int), BaseFee: new(big.Int),
} }
) )
mkTracer := func(name string, cfg json.RawMessage) *directory.Tracer { mkTracer := func(name string, cfg json.RawMessage) *tracers.Tracer {
tr, err := directory.DefaultDirectory.New(name, nil, cfg) tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
@ -294,7 +294,7 @@ func TestInternals(t *testing.T) {
for _, tc := range []struct { for _, tc := range []struct {
name string name string
code []byte code []byte
tracer *directory.Tracer tracer *tracers.Tracer
want string want string
}{ }{
{ {

View file

@ -16,7 +16,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
) )
@ -97,7 +97,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
defer state.Close() defer state.Close()
// Create the tracer, the EVM environment and run it // Create the tracer, the EVM environment and run it
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil { if err != nil {
return fmt.Errorf("failed to create call tracer: %v", err) return fmt.Errorf("failed to create call tracer: %v", err)
} }

View file

@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
) )
@ -113,7 +113,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
} }
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }

View file

@ -13,7 +13,7 @@
// //
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package directory package internal
import ( import (
"errors" "errors"
@ -62,6 +62,7 @@ func memoryCopy(m []byte, offset, size int64) (cpy []byte) {
return return
} }
// MemoryPtr returns a pointer to a slice of memory.
func MemoryPtr(m []byte, offset, size int64) []byte { func MemoryPtr(m []byte, offset, size int64) []byte {
if size == 0 { if size == 0 {
return nil return nil

View file

@ -13,7 +13,7 @@
// //
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package directory package internal
import ( import (
"testing" "testing"

View file

@ -25,7 +25,8 @@ import (
"github.com/dop251/goja" "github.com/dop251/goja"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/internal"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -44,16 +45,16 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
type ctorFn = func(*directory.Context, json.RawMessage) (*directory.Tracer, error) type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error)
lookup := func(code string) ctorFn { lookup := func(code string) ctorFn {
return func(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { return func(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
return newJsTracer(code, ctx, cfg) return newJsTracer(code, ctx, cfg)
} }
} }
for name, code := range assetTracers { for name, code := range assetTracers {
directory.DefaultDirectory.Register(name, lookup(code), true) tracers.DefaultDirectory.Register(name, lookup(code), true)
} }
directory.DefaultDirectory.RegisterJSEval(newJsTracer) tracers.DefaultDirectory.RegisterJSEval(newJsTracer)
} }
// bigIntProgram is compiled once and the exported function mostly invoked to convert // bigIntProgram is compiled once and the exported function mostly invoked to convert
@ -136,7 +137,7 @@ type jsTracer struct {
// The methods `result` and `fault` are required to be present. // The methods `result` and `fault` are required to be present.
// The methods `step`, `enter`, and `exit` are optional, but note that // The methods `step`, `enter`, and `exit` are optional, but note that
// `enter` and `exit` always go together. // `enter` and `exit` always go together.
func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
vm := goja.New() vm := goja.New()
// By default field names are exported to JS as is, i.e. capitalized. // By default field names are exported to JS as is, i.e. capitalized.
vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
@ -149,7 +150,7 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir
t.setBuiltinFunctions() t.setBuiltinFunctions()
if ctx == nil { if ctx == nil {
ctx = new(directory.Context) ctx = new(tracers.Context)
} }
if ctx.BlockHash != (common.Hash{}) { if ctx.BlockHash != (common.Hash{}) {
blockHash, err := t.toBuf(vm, ctx.BlockHash.Bytes()) blockHash, err := t.toBuf(vm, ctx.BlockHash.Bytes())
@ -220,7 +221,7 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir
t.frameResultValue = t.frameResult.setupObject() t.frameResultValue = t.frameResult.setupObject()
t.logValue = t.log.setupObject() t.logValue = t.log.setupObject()
return &directory.Tracer{ return &tracers.Tracer{
Hooks: &tracing.Hooks{ Hooks: &tracing.Hooks{
OnTxStart: t.OnTxStart, OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd, OnTxEnd: t.OnTxEnd,
@ -643,7 +644,7 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
if end < begin || begin < 0 { if end < begin || begin < 0 {
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end) return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
} }
slice, err := directory.GetMemoryCopyPadded(mo.memory, begin, end-begin) slice, err := internal.GetMemoryCopyPadded(mo.memory, begin, end-begin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -669,7 +670,7 @@ func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
if len(mo.memory) < int(addr)+32 || addr < 0 { if len(mo.memory) < int(addr)+32 || addr < 0 {
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", len(mo.memory), addr, 32) return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", len(mo.memory), addr, 32)
} }
return new(big.Int).SetBytes(directory.MemoryPtr(mo.memory, addr, 32)), nil return new(big.Int).SetBytes(internal.MemoryPtr(mo.memory, addr, 32)), nil
} }
func (mo *memoryObj) Length() int { func (mo *memoryObj) Length() int {
@ -709,7 +710,7 @@ func (s *stackObj) peek(idx int) (*big.Int, error) {
if len(s.stack) <= idx || idx < 0 { if len(s.stack) <= idx || idx < 0 {
return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack), idx) return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack), idx)
} }
return directory.StackBack(s.stack, idx).ToBig(), nil return internal.StackBack(s.stack, idx).ToBig(), nil
} }
func (s *stackObj) Length() int { func (s *stackObj) Length() int {

View file

@ -28,7 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -62,7 +62,7 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
} }
func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
var ( var (
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks}) env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks})
gasLimit uint64 = 31000 gasLimit uint64 = 31000
@ -265,14 +265,14 @@ func TestIsPrecompile(t *testing.T) {
func TestEnterExit(t *testing.T) { func TestEnterExit(t *testing.T) {
// test that either both or none of enter() and exit() are defined // test that either both or none of enter() and exit() are defined
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(directory.Context), nil); err == nil { if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil {
t.Fatal("tracer creation should've failed without exit() definition") t.Fatal("tracer creation should've failed without exit() definition")
} }
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(directory.Context), nil); err != nil { if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// test that the enter and exit method are correctly invoked and the values passed // test that the enter and exit method are correctly invoked and the values passed
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(directory.Context), nil) tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -294,7 +294,7 @@ func TestEnterExit(t *testing.T) {
func TestSetup(t *testing.T) { func TestSetup(t *testing.T) {
// Test empty config // Test empty config
_, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(directory.Context), nil) _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -304,12 +304,12 @@ func TestSetup(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// Test no setup func // Test no setup func
_, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(directory.Context), cfg) _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Test config value // Test config value
tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(directory.Context), cfg) tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -1,4 +1,4 @@
package live package tracers
import ( import (
"encoding/json" "encoding/json"
@ -9,21 +9,21 @@ import (
type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error) type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error)
// Directory is the collection of tracers which can be used // LiveDirectory is the collection of tracers which can be used
// during normal block import operations. // during normal block import operations.
var Directory = directory{elems: make(map[string]ctorFunc)} var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)}
type directory struct { type liveDirectory struct {
elems map[string]ctorFunc elems map[string]ctorFunc
} }
// Register registers a tracer constructor by name. // Register registers a tracer constructor by name.
func (d *directory) Register(name string, f ctorFunc) { func (d *liveDirectory) Register(name string, f ctorFunc) {
d.elems[name] = f d.elems[name] = f
} }
// New instantiates a tracer by name. // New instantiates a tracer by name.
func (d *directory) New(name string, config json.RawMessage) (*tracing.Hooks, error) { func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) {
if f, ok := d.elems[name]; ok { if f, ok := d.elems[name]; ok {
return f(config) return f(config)
} }

Some files were not shown because too many files have changed in this diff Show more