mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
Merge branch 'master' into simplify_StateTrie
This commit is contained in:
commit
aafdcc518a
133 changed files with 2481 additions and 3377 deletions
8
Makefile
8
Makefile
|
|
@ -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 all test lint fmt clean devtools help
|
.PHONY: geth evm all test lint fmt clean devtools help
|
||||||
|
|
||||||
GOBIN = ./build/bin
|
GOBIN = ./build/bin
|
||||||
GO ?= latest
|
GO ?= latest
|
||||||
|
|
@ -14,6 +14,12 @@ geth:
|
||||||
@echo "Done building."
|
@echo "Done building."
|
||||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||||
|
|
||||||
|
#? evm: Build evm.
|
||||||
|
evm:
|
||||||
|
$(GORUN) build/ci.go install ./cmd/evm
|
||||||
|
@echo "Done building."
|
||||||
|
@echo "Run \"$(GOBIN)/evm\" to launch evm."
|
||||||
|
|
||||||
#? all: Build all packages and executables.
|
#? all: Build all packages and executables.
|
||||||
all:
|
all:
|
||||||
$(GORUN) build/ci.go install
|
$(GORUN) build/ci.go install
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ var (
|
||||||
errBadInt16 = errors.New("abi: improperly encoded int16 value")
|
errBadInt16 = errors.New("abi: improperly encoded int16 value")
|
||||||
errBadInt32 = errors.New("abi: improperly encoded int32 value")
|
errBadInt32 = errors.New("abi: improperly encoded int32 value")
|
||||||
errBadInt64 = errors.New("abi: improperly encoded int64 value")
|
errBadInt64 = errors.New("abi: improperly encoded int64 value")
|
||||||
|
errInvalidSign = errors.New("abi: negatively-signed value cannot be packed into uint parameter")
|
||||||
)
|
)
|
||||||
|
|
||||||
// formatSliceString formats the reflection kind with the given slice size
|
// formatSliceString formats the reflection kind with the given slice size
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,16 @@ func packBytesSlice(bytes []byte, l int) []byte {
|
||||||
// t.
|
// t.
|
||||||
func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
||||||
switch t.T {
|
switch t.T {
|
||||||
case IntTy, UintTy:
|
case UintTy:
|
||||||
|
// make sure to not pack a negative value into a uint type.
|
||||||
|
if reflectValue.Kind() == reflect.Ptr {
|
||||||
|
val := new(big.Int).Set(reflectValue.Interface().(*big.Int))
|
||||||
|
if val.Sign() == -1 {
|
||||||
|
return nil, errInvalidSign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packNum(reflectValue), nil
|
||||||
|
case IntTy:
|
||||||
return packNum(reflectValue), nil
|
return packNum(reflectValue), nil
|
||||||
case StringTy:
|
case StringTy:
|
||||||
return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()), nil
|
return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()), nil
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,11 @@ func TestMethodPack(t *testing.T) {
|
||||||
if !bytes.Equal(packed, sig) {
|
if !bytes.Equal(packed, sig) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test that we can't pack a negative value for a parameter that is specified as a uint
|
||||||
|
if _, err := abi.Pack("send", big.NewInt(-1)); err == nil {
|
||||||
|
t.Fatal("expected error when trying to pack negative big.Int into uint256 value")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPackNumber(t *testing.T) {
|
func TestPackNumber(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1014,128 +1014,134 @@ func TestPackAndUnpackIncompatibleNumber(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
decodeType string
|
decodeType string
|
||||||
inputValue *big.Int
|
inputValue *big.Int
|
||||||
err error
|
unpackErr error
|
||||||
|
packErr error
|
||||||
expectValue interface{}
|
expectValue interface{}
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
decodeType: "uint8",
|
decodeType: "uint8",
|
||||||
inputValue: big.NewInt(math.MaxUint8 + 1),
|
inputValue: big.NewInt(math.MaxUint8 + 1),
|
||||||
err: errBadUint8,
|
unpackErr: errBadUint8,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint8",
|
decodeType: "uint8",
|
||||||
inputValue: big.NewInt(math.MaxUint8),
|
inputValue: big.NewInt(math.MaxUint8),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: uint8(math.MaxUint8),
|
expectValue: uint8(math.MaxUint8),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint16",
|
decodeType: "uint16",
|
||||||
inputValue: big.NewInt(math.MaxUint16 + 1),
|
inputValue: big.NewInt(math.MaxUint16 + 1),
|
||||||
err: errBadUint16,
|
unpackErr: errBadUint16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint16",
|
decodeType: "uint16",
|
||||||
inputValue: big.NewInt(math.MaxUint16),
|
inputValue: big.NewInt(math.MaxUint16),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: uint16(math.MaxUint16),
|
expectValue: uint16(math.MaxUint16),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint32",
|
decodeType: "uint32",
|
||||||
inputValue: big.NewInt(math.MaxUint32 + 1),
|
inputValue: big.NewInt(math.MaxUint32 + 1),
|
||||||
err: errBadUint32,
|
unpackErr: errBadUint32,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint32",
|
decodeType: "uint32",
|
||||||
inputValue: big.NewInt(math.MaxUint32),
|
inputValue: big.NewInt(math.MaxUint32),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: uint32(math.MaxUint32),
|
expectValue: uint32(math.MaxUint32),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint64",
|
decodeType: "uint64",
|
||||||
inputValue: maxU64Plus1,
|
inputValue: maxU64Plus1,
|
||||||
err: errBadUint64,
|
unpackErr: errBadUint64,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint64",
|
decodeType: "uint64",
|
||||||
inputValue: maxU64,
|
inputValue: maxU64,
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: uint64(math.MaxUint64),
|
expectValue: uint64(math.MaxUint64),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "uint256",
|
decodeType: "uint256",
|
||||||
inputValue: maxU64Plus1,
|
inputValue: maxU64Plus1,
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: maxU64Plus1,
|
expectValue: maxU64Plus1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int8",
|
decodeType: "int8",
|
||||||
inputValue: big.NewInt(math.MaxInt8 + 1),
|
inputValue: big.NewInt(math.MaxInt8 + 1),
|
||||||
err: errBadInt8,
|
unpackErr: errBadInt8,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int8",
|
|
||||||
inputValue: big.NewInt(math.MinInt8 - 1),
|
inputValue: big.NewInt(math.MinInt8 - 1),
|
||||||
err: errBadInt8,
|
packErr: errInvalidSign,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int8",
|
decodeType: "int8",
|
||||||
inputValue: big.NewInt(math.MaxInt8),
|
inputValue: big.NewInt(math.MaxInt8),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: int8(math.MaxInt8),
|
expectValue: int8(math.MaxInt8),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int16",
|
decodeType: "int16",
|
||||||
inputValue: big.NewInt(math.MaxInt16 + 1),
|
inputValue: big.NewInt(math.MaxInt16 + 1),
|
||||||
err: errBadInt16,
|
unpackErr: errBadInt16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int16",
|
|
||||||
inputValue: big.NewInt(math.MinInt16 - 1),
|
inputValue: big.NewInt(math.MinInt16 - 1),
|
||||||
err: errBadInt16,
|
packErr: errInvalidSign,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int16",
|
decodeType: "int16",
|
||||||
inputValue: big.NewInt(math.MaxInt16),
|
inputValue: big.NewInt(math.MaxInt16),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: int16(math.MaxInt16),
|
expectValue: int16(math.MaxInt16),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int32",
|
decodeType: "int32",
|
||||||
inputValue: big.NewInt(math.MaxInt32 + 1),
|
inputValue: big.NewInt(math.MaxInt32 + 1),
|
||||||
err: errBadInt32,
|
unpackErr: errBadInt32,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int32",
|
|
||||||
inputValue: big.NewInt(math.MinInt32 - 1),
|
inputValue: big.NewInt(math.MinInt32 - 1),
|
||||||
err: errBadInt32,
|
packErr: errInvalidSign,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int32",
|
decodeType: "int32",
|
||||||
inputValue: big.NewInt(math.MaxInt32),
|
inputValue: big.NewInt(math.MaxInt32),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: int32(math.MaxInt32),
|
expectValue: int32(math.MaxInt32),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int64",
|
decodeType: "int64",
|
||||||
inputValue: new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)),
|
inputValue: new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)),
|
||||||
err: errBadInt64,
|
unpackErr: errBadInt64,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int64",
|
|
||||||
inputValue: new(big.Int).Sub(big.NewInt(math.MinInt64), big.NewInt(1)),
|
inputValue: new(big.Int).Sub(big.NewInt(math.MinInt64), big.NewInt(1)),
|
||||||
err: errBadInt64,
|
packErr: errInvalidSign,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
decodeType: "int64",
|
decodeType: "int64",
|
||||||
inputValue: big.NewInt(math.MaxInt64),
|
inputValue: big.NewInt(math.MaxInt64),
|
||||||
err: nil,
|
unpackErr: nil,
|
||||||
expectValue: int64(math.MaxInt64),
|
expectValue: int64(math.MaxInt64),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, testCase := range cases {
|
for i, testCase := range cases {
|
||||||
packed, err := encodeABI.Pack(testCase.inputValue)
|
packed, err := encodeABI.Pack(testCase.inputValue)
|
||||||
if err != nil {
|
if testCase.packErr != nil {
|
||||||
panic(err)
|
if err == nil {
|
||||||
|
t.Fatalf("expected packing of testcase input value to fail")
|
||||||
|
}
|
||||||
|
if err != testCase.packErr {
|
||||||
|
t.Fatalf("expected error '%v', got '%v'", testCase.packErr, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil && err != testCase.packErr {
|
||||||
|
panic(fmt.Errorf("unexpected error packing test-case input: %v", err))
|
||||||
}
|
}
|
||||||
ty, err := NewType(testCase.decodeType, "", nil)
|
ty, err := NewType(testCase.decodeType, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1145,8 +1151,8 @@ func TestPackAndUnpackIncompatibleNumber(t *testing.T) {
|
||||||
{Type: ty},
|
{Type: ty},
|
||||||
}
|
}
|
||||||
decoded, err := decodeABI.Unpack(packed)
|
decoded, err := decodeABI.Unpack(packed)
|
||||||
if err != testCase.err {
|
if err != testCase.unpackErr {
|
||||||
t.Fatalf("Expected error %v, actual error %v. case %d", testCase.err, err, i)
|
t.Fatalf("Expected error %v, actual error %v. case %d", testCase.unpackErr, err, i)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -93,9 +93,6 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
||||||
|
|
||||||
// Close terminates the account manager's internal notification processes.
|
// Close terminates the account manager's internal notification processes.
|
||||||
func (am *Manager) Close() error {
|
func (am *Manager) Close() error {
|
||||||
for _, w := range am.wallets {
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
am.quit <- errc
|
am.quit <- errc
|
||||||
return <-errc
|
return <-errc
|
||||||
|
|
@ -149,6 +146,10 @@ func (am *Manager) update() {
|
||||||
am.lock.Unlock()
|
am.lock.Unlock()
|
||||||
close(event.processed)
|
close(event.processed)
|
||||||
case errc := <-am.quit:
|
case errc := <-am.quit:
|
||||||
|
// Close all owned wallets
|
||||||
|
for _, w := range am.wallets {
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
// Manager terminating, return
|
// Manager terminating, return
|
||||||
errc <- nil
|
errc <- nil
|
||||||
// Signals event emitters the loop is not receiving values
|
// Signals event emitters the loop is not receiving values
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,11 @@ type BlobAndProofV1 struct {
|
||||||
Proof hexutil.Bytes `json:"proof"`
|
Proof hexutil.Bytes `json:"proof"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BlobAndProofV2 struct {
|
||||||
|
Blob hexutil.Bytes `json:"blob"`
|
||||||
|
CellProofs []hexutil.Bytes `json:"proofs"`
|
||||||
|
}
|
||||||
|
|
||||||
// JSON type overrides for ExecutionPayloadEnvelope.
|
// JSON type overrides for ExecutionPayloadEnvelope.
|
||||||
type executionPayloadEnvelopeMarshaling struct {
|
type executionPayloadEnvelopeMarshaling struct {
|
||||||
BlockValue *hexutil.Big
|
BlockValue *hexutil.Big
|
||||||
|
|
@ -331,7 +336,9 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||||
for j := range sidecar.Blobs {
|
for j := range sidecar.Blobs {
|
||||||
bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:]))
|
bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:]))
|
||||||
bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:]))
|
bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:]))
|
||||||
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
|
}
|
||||||
|
for _, proof := range sidecar.Proofs {
|
||||||
|
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(proof[:]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
56
beacon/engine/types_test.go
Normal file
56
beacon/engine/types_test.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2025 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 engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBlobs(t *testing.T) {
|
||||||
|
var (
|
||||||
|
emptyBlob = new(kzg4844.Blob)
|
||||||
|
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
|
||||||
|
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
|
||||||
|
emptyCellProof, _ = kzg4844.ComputeCellProofs(emptyBlob)
|
||||||
|
)
|
||||||
|
header := types.Header{}
|
||||||
|
block := types.NewBlock(&header, &types.Body{}, nil, nil)
|
||||||
|
|
||||||
|
sidecarWithoutCellProofs := &types.BlobTxSidecar{
|
||||||
|
Blobs: []kzg4844.Blob{*emptyBlob},
|
||||||
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||||
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||||
|
}
|
||||||
|
env := BlockToExecutableData(block, common.Big0, []*types.BlobTxSidecar{sidecarWithoutCellProofs}, nil)
|
||||||
|
if len(env.BlobsBundle.Proofs) != 1 {
|
||||||
|
t.Fatalf("Expect 1 proof in blobs bundle, got %v", len(env.BlobsBundle.Proofs))
|
||||||
|
}
|
||||||
|
|
||||||
|
sidecarWithCellProofs := &types.BlobTxSidecar{
|
||||||
|
Blobs: []kzg4844.Blob{*emptyBlob},
|
||||||
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||||
|
Proofs: emptyCellProof,
|
||||||
|
}
|
||||||
|
env = BlockToExecutableData(block, common.Big0, []*types.BlobTxSidecar{sidecarWithCellProofs}, nil)
|
||||||
|
if len(env.BlobsBundle.Proofs) != 128 {
|
||||||
|
t.Fatalf("Expect 128 proofs in blobs bundle, got %v", len(env.BlobsBundle.Proofs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,54 +5,54 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/
|
||||||
58afb92a0075a2cb7c4dec1281f7cb88b21b02afbedad096b580f3f8cc14c54c fixtures_develop.tar.gz
|
58afb92a0075a2cb7c4dec1281f7cb88b21b02afbedad096b580f3f8cc14c54c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.24.3
|
# version:golang 1.24.4
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
229c08b600b1446798109fae1f569228102c8473caba8104b6418cb5bc032878 go1.24.3.src.tar.gz
|
5a86a83a31f9fa81490b8c5420ac384fd3d95a3e71fba665c7b3f95d1dfef2b4 go1.24.4.src.tar.gz
|
||||||
6f6901497547db3b77c14f7f953fbcef9fa5fb84199ee2ee14a5686e66bed5a6 go1.24.3.aix-ppc64.tar.gz
|
0d2af78e3b6e08f8013dbbdb26ae33052697b6b72e03ec17d496739c2a1aed68 go1.24.4.aix-ppc64.tar.gz
|
||||||
a05fa7e4043a4fec66897135219e3b8ab2202b5ef351c60c2fbb531dfb8f2900 go1.24.3.darwin-amd64.pkg
|
69bef555e114b4a2252452b6e7049afc31fbdf2d39790b669165e89525cd3f5c go1.24.4.darwin-amd64.tar.gz
|
||||||
13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b go1.24.3.darwin-amd64.tar.gz
|
c4d74453a26f488bdb4b0294da4840d9020806de4661785334eb6d1803ee5c27 go1.24.4.darwin-amd64.pkg
|
||||||
97055ff4214043b39dc32e043fdd5c565df7c0a4e2fc0174e779a134c347ae0e go1.24.3.darwin-arm64.pkg
|
27973684b515eaf461065054e6b572d9390c05e69ba4a423076c160165336470 go1.24.4.darwin-arm64.tar.gz
|
||||||
64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb go1.24.3.darwin-arm64.tar.gz
|
2fe1f8746745c4bfebd494583aaef24cad42594f6d25ed67856879d567ee66e7 go1.24.4.darwin-arm64.pkg
|
||||||
32de3fd44d5055973978436a7f1f0ffbaae85c1b603ec6105e5c38d8a674c721 go1.24.3.dragonfly-amd64.tar.gz
|
70b2de9c1cafe5af7be3eb8f80753cce0501ef300db3f3bd59be7ccc464234e1 go1.24.4.dragonfly-amd64.tar.gz
|
||||||
9fe6101b3797919bd7337ee5ce591954f85d59db7ae88983904db29fd64c3dd1 go1.24.3.freebsd-386.tar.gz
|
8d529839db29ee171505b89dc9c3de76003a4ab56202d84bddbbecacbfb6d7c9 go1.24.4.freebsd-386.tar.gz
|
||||||
6ccf4cca287e90cc28cd7954b6172f5d177a17e20b072b65f7f39636c325e2fb go1.24.3.freebsd-amd64.tar.gz
|
6cbc3ad6cc21bdcc7283824d3ac0e85512c02022f6a35eb2e844882ea6e8448c go1.24.4.freebsd-amd64.tar.gz
|
||||||
ce45ebf389066f82a7b056b66dd650efb51fde6f8bf92a2a3ab6990f02788ebf go1.24.3.freebsd-arm.tar.gz
|
d49ae050c20aff646a7641dd903f03eb674570790b90ffb298076c4d41e36655 go1.24.4.freebsd-arm.tar.gz
|
||||||
8f6494a12a874d0ea57c67987829359e016960ce3ba0673273609d6ac2af589a go1.24.3.freebsd-arm64.tar.gz
|
e31924abef2a28456b7103c0a5d333dcc11ecf19e76d5de1a383ad5fe0b42457 go1.24.4.freebsd-arm64.tar.gz
|
||||||
f9db392560cf0851f0bc8f2190e1978e01b4603038c27fecfc8658a695b71616 go1.24.3.freebsd-riscv64.tar.gz
|
b5bca135eae8ebddf22972611ac1c58ae9fbb5979fd953cc5245c5b1b2517546 go1.24.4.freebsd-riscv64.tar.gz
|
||||||
01717fff64c5d98457272002fa825d0a15e307bf6e189f2b0c23817fa033b61c go1.24.3.illumos-amd64.tar.gz
|
7d5efda511ff7e3114b130acee5d0bffbb078fedbfa9b2c1b6a807107e1ca23a go1.24.4.illumos-amd64.tar.gz
|
||||||
41b1051063e68cbd2b919bf12326764fe33937cf1d32b5c529dd1a4f43dce578 go1.24.3.linux-386.tar.gz
|
130c9b061082eca15513e595e9952a2ded32e737e609dd0e49f7dfa74eba026d go1.24.4.linux-386.tar.gz
|
||||||
3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 go1.24.3.linux-amd64.tar.gz
|
77e5da33bb72aeaef1ba4418b6fe511bc4d041873cbf82e5aa6318740df98717 go1.24.4.linux-amd64.tar.gz
|
||||||
a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 go1.24.3.linux-arm64.tar.gz
|
d5501ee5aca0f258d5fe9bfaed401958445014495dc115f202d43d5210b45241 go1.24.4.linux-arm64.tar.gz
|
||||||
17a392d7e826625dd12a32099df0b00b85c32d8132ed86fe917183ee5c3f88ed go1.24.3.linux-armv6l.tar.gz
|
6a554e32301cecae3162677e66d4264b81b3b1a89592dd1b7b5c552c7a49fe37 go1.24.4.linux-armv6l.tar.gz
|
||||||
e4b003c04c902edc140153d279b42167f1ad7c229f48f1f729bbef5e65e88d1f go1.24.3.linux-loong64.tar.gz
|
b208eb25fe244408cbe269ed426454bc46e59d0e0a749b6240d39e884e969875 go1.24.4.linux-loong64.tar.gz
|
||||||
1c79d89edf835edf9d4336ccea7cb89bc5c0ca82b12b36b218d599a5400d60fe go1.24.3.linux-mips.tar.gz
|
fddfcb28fd36fe63d2ae181026798f86f3bbd3a7bb0f1e1f617dd3d604bf3fe4 go1.24.4.linux-mips.tar.gz
|
||||||
0b64fe147d69f4d681d8e8a035c760477531432f83d831f18d37cb9bf3652488 go1.24.3.linux-mips64.tar.gz
|
7934b924d5ab8c8ae3134a09a6ae74d3c39f63f6c4322ec289364dbbf0bac3ca go1.24.4.linux-mips64.tar.gz
|
||||||
396b784c255b64512dc00c302c053e43a3cbfc77518664c6ac5569aafad4d1e6 go1.24.3.linux-mips64le.tar.gz
|
fa763d8673f94d6e534bb72c3cf675d4c2b8da4a6da42a89f08c5586106db39c go1.24.4.linux-mips64le.tar.gz
|
||||||
93898313887f14e8efbe9d7386d5da4792b2d6c492bee562993fd4c9daa75c6d go1.24.3.linux-mipsle.tar.gz
|
84363dbfe49b41d43df84420a09bd53a4770053d63bfa509868c46a5f8eb3ff7 go1.24.4.linux-mipsle.tar.gz
|
||||||
873ae3a6a6655a7b6f820e095d9965507e8dfd3cf76bc92d75c564ecbca385f6 go1.24.3.linux-ppc64.tar.gz
|
28fcbd5d3b56493606873c33f2b4bdd84ba93c633f37313613b5a1e6495c6fe5 go1.24.4.linux-ppc64.tar.gz
|
||||||
341a749d168f47b1d4dad25e32cae70849b7ceed7c290823b853c9e6b0df0856 go1.24.3.linux-ppc64le.tar.gz
|
9ca4afef813a2578c23843b640ae0290aa54b2e3c950a6cc4c99e16a57dec2ec go1.24.4.linux-ppc64le.tar.gz
|
||||||
fa482f53ccb4ba280316b8c5751ea67291507280d9166f2a38fe4d9b5d5fb64b go1.24.3.linux-riscv64.tar.gz
|
1d7034f98662d8f2c8abd7c700ada4093acb4f9c00e0e51a30344821d0785c77 go1.24.4.linux-riscv64.tar.gz
|
||||||
a87b0c2a079a0bece1620fb29a00e02b4dba17507850f837e754af7d57cda282 go1.24.3.linux-s390x.tar.gz
|
0449f3203c39703ab27684be763e9bb78ca9a051e0e4176727aead9461b6deb5 go1.24.4.linux-s390x.tar.gz
|
||||||
63155382308db1306200aff7821aa26bf2a2dda23537dd637a9704b485b6ddf0 go1.24.3.netbsd-386.tar.gz
|
954b49ccc2cfcf4b5f7cd33ff662295e0d3b74e7590c8e25fc2abb30bce120ba go1.24.4.netbsd-386.tar.gz
|
||||||
fe2c5c79482958b867c08a4fc2a10a998de9c0206b08d5b3ebcb2232e8d2777c go1.24.3.netbsd-amd64.tar.gz
|
370fabcdfee7c18857c96fdd5b706e025d4fb86a208da88ba56b1493b35498e9 go1.24.4.netbsd-amd64.tar.gz
|
||||||
e8ff77aef21521b5dd94e44282a3243309b80717414cf12f72835a45886a049f go1.24.3.netbsd-arm.tar.gz
|
7935ef95d4d1acc48587b1eb4acab98b0a7d9569736a32398b9c1d2e89026865 go1.24.4.netbsd-arm.tar.gz
|
||||||
b337fbaf82822685940ffaa76fbcf4be5d2f0258bc819cd80bc408b491f45c04 go1.24.3.netbsd-arm64.tar.gz
|
ead78fd0fa29fbb176cc83f1caa54032e1a44f842affa56a682c647e0759f237 go1.24.4.netbsd-arm64.tar.gz
|
||||||
c1bb9dd8418480aa7f65452b08de3759da3bf89702be71b5a9fc084836b24ad5 go1.24.3.openbsd-386.tar.gz
|
913e217394b851a636b99de175f0c2f9ab9938b41c557f047168f77ee485d776 go1.24.4.openbsd-386.tar.gz
|
||||||
531218de748b0caaf6d1ad18921206fc12baaa89bf483a0a5e60a571c206fe6f go1.24.3.openbsd-amd64.tar.gz
|
24568da3dcbcdb24ec18b631f072faf0f3763e3d04f79032dc56ad9ec35379c4 go1.24.4.openbsd-amd64.tar.gz
|
||||||
bcd0dc959986fc346969b5d4111c3c8031882d8bf8d87a2c2ecf1328962a91f2 go1.24.3.openbsd-arm.tar.gz
|
45abf523f870632417ab007de3841f64dd906bde546ffc8c6380ccbe91c7fb73 go1.24.4.openbsd-arm.tar.gz
|
||||||
00ee6f8f1c41fd2e28ad386bd7e39acce7cab84af6de835855b29d1c597335c4 go1.24.3.openbsd-arm64.tar.gz
|
7c57c69b5dd1e946b28a3034c285240a48e2861bdcb50b7d9c0ed61bcf89c879 go1.24.4.openbsd-arm64.tar.gz
|
||||||
9f4ec0a9203ed3c54ce1a2a390ad3d45838cdb7efd85baeff857e37dfde04edd go1.24.3.openbsd-ppc64.tar.gz
|
91ed711f704829372d6931e1897631ef40288b8f9e3cd6ef4a24df7126d1066a go1.24.4.openbsd-ppc64.tar.gz
|
||||||
da4d6f80e2373250d8c31c32dcd1e08775c327c0d610923604660cc0e07e8cba go1.24.3.openbsd-riscv64.tar.gz
|
de5e270d971c8790e8880168d56a2ea103979927c10ded136d792bbdf9bce3d3 go1.24.4.openbsd-riscv64.tar.gz
|
||||||
f5d02149132eedda6c2d46b360d7da462b8a5f9e3f8567db100c2d7bff0ddcd7 go1.24.3.plan9-386.tar.gz
|
ff429d03f00bcd32a50f445320b8329d0fadb2a2fff899c11e95e0922a82c543 go1.24.4.plan9-386.tar.gz
|
||||||
175f3d79f4762a3c545d2c6393bf6b8bac24e838026869dafab06b930735c94f go1.24.3.plan9-amd64.tar.gz
|
39d6363a43fd16b60ae9ad7346a264e982e4fa653dee3b45f83e03cd2f7a6647 go1.24.4.plan9-amd64.tar.gz
|
||||||
d1e4ac15095da1611659261c2228c2058756cf87d61d9fad262f76755ef26849 go1.24.3.plan9-arm.tar.gz
|
1964ae2571259de77b930e97f2891aa92706ff81aac9909d45bb107b0fab16c8 go1.24.4.plan9-arm.tar.gz
|
||||||
e644220a6ced3c07a7acc1364193cb709a97737dd8b6792a07a8ec6d9996713e go1.24.3.solaris-amd64.tar.gz
|
a7f9af424e8fb87886664754badca459513f64f6a321d17f1d219b8edf519821 go1.24.4.solaris-amd64.tar.gz
|
||||||
0d7e7dc0a31ba0cdd487415709d03b02fc9490ef111e8dfd22788a6d63316f37 go1.24.3.windows-386.msi
|
d454d3cb144432f1726bf00e28c6017e78ccb256a8d01b8e3fb1b2e6b5650f28 go1.24.4.windows-386.zip
|
||||||
c27c463a61ab849266baa0c17a6c5c4256a574ab642f609ba25c96ec965dc184 go1.24.3.windows-386.zip
|
966ecace1cdbb3497a2b930bdb0f90c3ad32922fa1a7c655b2d4bbeb7e4ac308 go1.24.4.windows-386.msi
|
||||||
d5b7637e7e138be877d96a4501709d480e050d86a8f402bc950e72112b5aedc5 go1.24.3.windows-amd64.msi
|
b751a1136cb9d8a2e7ebb22c538c4f02c09b98138c7c8bfb78a54a4566c013b1 go1.24.4.windows-amd64.zip
|
||||||
be9787cb08998b1860fe3513e48a5fe5b96302d358a321b58e651184fa9638b3 go1.24.3.windows-amd64.zip
|
0cbb6e83865747dbe69b3d4155f92e88fcf336ff5d70182dba145e9d7bd3d8f6 go1.24.4.windows-amd64.msi
|
||||||
7efde2e5e8468e9caf2c7fc94f4da78a726a5031a1ed63acff7899527cdddff6 go1.24.3.windows-arm64.msi
|
d17da51bc85bd010754a4063215d15d2c033cc289d67ca9201a03c9041b2969d go1.24.4.windows-arm64.zip
|
||||||
eec9fa736056b54dd88ecb669db2bfad39b0c48f6f9080f036dfa1ca42dc4bb5 go1.24.3.windows-arm64.zip
|
47dbe734b6a829de45654648a7abcf05bdceef5c80e03ea0b208eeebef75a852 go1.24.4.windows-arm64.msi
|
||||||
|
|
||||||
# version:golangci 2.0.2
|
# version:golangci 2.0.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
// Copyright 2024 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var jt vm.JumpTable
|
|
||||||
|
|
||||||
const initcode = "INITCODE"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
jt = vm.NewEOFInstructionSetForTesting()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
hexFlag = &cli.StringFlag{
|
|
||||||
Name: "hex",
|
|
||||||
Usage: "Single container data parse and validation",
|
|
||||||
}
|
|
||||||
refTestFlag = &cli.StringFlag{
|
|
||||||
Name: "test",
|
|
||||||
Usage: "Path to EOF validation reference test.",
|
|
||||||
}
|
|
||||||
eofParseCommand = &cli.Command{
|
|
||||||
Name: "eofparse",
|
|
||||||
Aliases: []string{"eof"},
|
|
||||||
Usage: "Parses hex eof container and returns validation errors (if any)",
|
|
||||||
Action: eofParseAction,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
hexFlag,
|
|
||||||
refTestFlag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
eofDumpCommand = &cli.Command{
|
|
||||||
Name: "eofdump",
|
|
||||||
Usage: "Parses hex eof container and prints out human-readable representation of the container.",
|
|
||||||
Action: eofDumpAction,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
hexFlag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func eofParseAction(ctx *cli.Context) error {
|
|
||||||
// If `--test` is set, parse and validate the reference test at the provided path.
|
|
||||||
if ctx.IsSet(refTestFlag.Name) {
|
|
||||||
var (
|
|
||||||
file = ctx.String(refTestFlag.Name)
|
|
||||||
executedTests int
|
|
||||||
passedTests int
|
|
||||||
)
|
|
||||||
err := filepath.Walk(file, func(path string, info fs.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Debug("Executing test", "name", info.Name())
|
|
||||||
passed, tot, err := executeTest(path)
|
|
||||||
passedTests += passed
|
|
||||||
executedTests += tot
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("Executed tests", "passed", passedTests, "total executed", executedTests)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// If `--hex` is set, parse and validate the hex string argument.
|
|
||||||
if ctx.IsSet(hexFlag.Name) {
|
|
||||||
if _, err := parseAndValidate(ctx.String(hexFlag.Name), false); err != nil {
|
|
||||||
return fmt.Errorf("err: %w", err)
|
|
||||||
}
|
|
||||||
fmt.Println("OK")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// If neither are passed in, read input from stdin.
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024)
|
|
||||||
for scanner.Scan() {
|
|
||||||
l := strings.TrimSpace(scanner.Text())
|
|
||||||
if strings.HasPrefix(l, "#") || l == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := parseAndValidate(l, false); err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Println("OK")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type refTests struct {
|
|
||||||
Vectors map[string]eOFTest `json:"vectors"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type eOFTest struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Results map[string]etResult `json:"results"`
|
|
||||||
ContainerKind string `json:"containerKind"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type etResult struct {
|
|
||||||
Result bool `json:"result"`
|
|
||||||
Exception string `json:"exception,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeTest(path string) (int, int, error) {
|
|
||||||
src, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
var testsByName map[string]refTests
|
|
||||||
if err := json.Unmarshal(src, &testsByName); err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
passed, total := 0, 0
|
|
||||||
for testsName, tests := range testsByName {
|
|
||||||
for name, tt := range tests.Vectors {
|
|
||||||
for fork, r := range tt.Results {
|
|
||||||
total++
|
|
||||||
_, err := parseAndValidate(tt.Code, tt.ContainerKind == initcode)
|
|
||||||
if r.Result && err != nil {
|
|
||||||
log.Error("Test failure, expected validation success", "name", testsName, "idx", name, "fork", fork, "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !r.Result && err == nil {
|
|
||||||
log.Error("Test failure, expected validation error", "name", testsName, "idx", name, "fork", fork, "have err", r.Exception, "err", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
passed++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return passed, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAndValidate(s string, isInitCode bool) (*vm.Container, error) {
|
|
||||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
|
||||||
s = s[2:]
|
|
||||||
}
|
|
||||||
b, err := hex.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to decode data: %w", err)
|
|
||||||
}
|
|
||||||
return parse(b, isInitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(b []byte, isInitCode bool) (*vm.Container, error) {
|
|
||||||
var c vm.Container
|
|
||||||
if err := c.UnmarshalBinary(b, isInitCode); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := c.ValidateCode(&jt, isInitCode); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func eofDumpAction(ctx *cli.Context) error {
|
|
||||||
// If `--hex` is set, parse and validate the hex string argument.
|
|
||||||
if ctx.IsSet(hexFlag.Name) {
|
|
||||||
return eofDump(ctx.String(hexFlag.Name))
|
|
||||||
}
|
|
||||||
// Otherwise read from stdin
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024)
|
|
||||||
for scanner.Scan() {
|
|
||||||
l := strings.TrimSpace(scanner.Text())
|
|
||||||
if strings.HasPrefix(l, "#") || l == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := eofDump(l); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
return scanner.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func eofDump(hexdata string) error {
|
|
||||||
if len(hexdata) >= 2 && strings.HasPrefix(hexdata, "0x") {
|
|
||||||
hexdata = hexdata[2:]
|
|
||||||
}
|
|
||||||
b, err := hex.DecodeString(hexdata)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to decode data: %w", err)
|
|
||||||
}
|
|
||||||
var c vm.Container
|
|
||||||
if err := c.UnmarshalBinary(b, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(c.String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
// Copyright 2024 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func FuzzEofParsing(f *testing.F) {
|
|
||||||
// Seed with corpus from execution-spec-tests
|
|
||||||
for i := 0; ; i++ {
|
|
||||||
fname := fmt.Sprintf("testdata/eof/eof_corpus_%d.txt", i)
|
|
||||||
corpus, err := os.Open(fname)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
f.Logf("Reading seed data from %v", fname)
|
|
||||||
scanner := bufio.NewScanner(corpus)
|
|
||||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
|
||||||
for scanner.Scan() {
|
|
||||||
s := scanner.Text()
|
|
||||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
|
||||||
s = s[2:]
|
|
||||||
}
|
|
||||||
b, err := hex.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // rotten corpus
|
|
||||||
}
|
|
||||||
f.Add(b)
|
|
||||||
}
|
|
||||||
corpus.Close()
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
panic(err) // rotten corpus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// And do the fuzzing
|
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
|
||||||
var (
|
|
||||||
jt = vm.NewEOFInstructionSetForTesting()
|
|
||||||
c vm.Container
|
|
||||||
)
|
|
||||||
cpy := common.CopyBytes(data)
|
|
||||||
if err := c.UnmarshalBinary(data, true); err == nil {
|
|
||||||
c.ValidateCode(&jt, true)
|
|
||||||
if have := c.MarshalBinary(); !bytes.Equal(have, data) {
|
|
||||||
t.Fatal("Unmarshal-> Marshal failure!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := c.UnmarshalBinary(data, false); err == nil {
|
|
||||||
c.ValidateCode(&jt, false)
|
|
||||||
if have := c.MarshalBinary(); !bytes.Equal(have, data) {
|
|
||||||
t.Fatal("Unmarshal-> Marshal failure!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !bytes.Equal(cpy, data) {
|
|
||||||
panic("data modified during unmarshalling")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEofParseInitcode(t *testing.T) {
|
|
||||||
testEofParse(t, true, "testdata/eof/results.initcode.txt")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEofParseRegular(t *testing.T) {
|
|
||||||
testEofParse(t, false, "testdata/eof/results.regular.txt")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testEofParse(t *testing.T, isInitCode bool, wantFile string) {
|
|
||||||
var wantFn func() string
|
|
||||||
var wantLoc = 0
|
|
||||||
{ // Configure the want-reader
|
|
||||||
wants, err := os.Open(wantFile)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
scanner := bufio.NewScanner(wants)
|
|
||||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
|
||||||
wantFn = func() string {
|
|
||||||
if scanner.Scan() {
|
|
||||||
wantLoc++
|
|
||||||
return scanner.Text()
|
|
||||||
}
|
|
||||||
return "end of file reached"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; ; i++ {
|
|
||||||
fname := fmt.Sprintf("testdata/eof/eof_corpus_%d.txt", i)
|
|
||||||
corpus, err := os.Open(fname)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t.Logf("# Reading seed data from %v", fname)
|
|
||||||
scanner := bufio.NewScanner(corpus)
|
|
||||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
|
||||||
line := 1
|
|
||||||
for scanner.Scan() {
|
|
||||||
s := scanner.Text()
|
|
||||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
|
||||||
s = s[2:]
|
|
||||||
}
|
|
||||||
b, err := hex.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // rotten corpus
|
|
||||||
}
|
|
||||||
have := "OK"
|
|
||||||
if _, err := parse(b, isInitCode); err != nil {
|
|
||||||
have = fmt.Sprintf("ERR: %v", err)
|
|
||||||
}
|
|
||||||
if false { // Change this to generate the want-output
|
|
||||||
fmt.Printf("%v\n", have)
|
|
||||||
} else {
|
|
||||||
want := wantFn()
|
|
||||||
if have != want {
|
|
||||||
if len(want) > 100 {
|
|
||||||
want = want[:100]
|
|
||||||
}
|
|
||||||
if len(b) > 100 {
|
|
||||||
b = b[:100]
|
|
||||||
}
|
|
||||||
t.Errorf("%v:%d\n%v\ninput %x\nisInit: %v\nhave: %q\nwant: %q\n",
|
|
||||||
fname, line, fmt.Sprintf("%v:%d", wantFile, wantLoc), b, isInitCode, have, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line++
|
|
||||||
}
|
|
||||||
corpus.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEofParse(b *testing.B) {
|
|
||||||
corpus, err := os.Open("testdata/eof/eof_benches.txt")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer corpus.Close()
|
|
||||||
scanner := bufio.NewScanner(corpus)
|
|
||||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024)
|
|
||||||
line := 1
|
|
||||||
for scanner.Scan() {
|
|
||||||
s := scanner.Text()
|
|
||||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
|
|
||||||
s = s[2:]
|
|
||||||
}
|
|
||||||
data, err := hex.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err) // rotten corpus
|
|
||||||
}
|
|
||||||
b.Run(fmt.Sprintf("test-%d", line), func(b *testing.B) {
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = parse(data, false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
line++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -303,7 +303,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash)
|
receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash, vmContext.Time)
|
||||||
receipt.Bloom = types.CreateBloom(receipt)
|
receipt.Bloom = types.CreateBloom(receipt)
|
||||||
|
|
||||||
// These three are non-consensus fields:
|
// These three are non-consensus fields:
|
||||||
|
|
|
||||||
|
|
@ -210,8 +210,6 @@ func init() {
|
||||||
stateTransitionCommand,
|
stateTransitionCommand,
|
||||||
transactionCommand,
|
transactionCommand,
|
||||||
blockBuilderCommand,
|
blockBuilderCommand,
|
||||||
eofParseCommand,
|
|
||||||
eofDumpCommand,
|
|
||||||
}
|
}
|
||||||
app.Before = func(ctx *cli.Context) error {
|
app.Before = func(ctx *cli.Context) error {
|
||||||
flags.MigrateGlobalFlags(ctx)
|
flags.MigrateGlobalFlags(ctx)
|
||||||
|
|
|
||||||
|
|
@ -723,8 +723,12 @@ func downloadEra(ctx *cli.Context) error {
|
||||||
// Resolve the destination directory.
|
// Resolve the destination directory.
|
||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
ancients := stack.ResolveAncient("chaindata", "")
|
ancients := stack.ResolveAncient("chaindata", "")
|
||||||
dir := filepath.Join(ancients, "era")
|
dir := filepath.Join(ancients, rawdb.ChainFreezerName, "era")
|
||||||
|
if ctx.IsSet(utils.EraFlag.Name) {
|
||||||
|
dir = filepath.Join(ancients, ctx.String(utils.EraFlag.Name))
|
||||||
|
}
|
||||||
|
|
||||||
baseURL := ctx.String(eraServerFlag.Name)
|
baseURL := ctx.String(eraServerFlag.Name)
|
||||||
if baseURL == "" {
|
if baseURL == "" {
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ var (
|
||||||
}
|
}
|
||||||
SepoliaFlag = &cli.BoolFlag{
|
SepoliaFlag = &cli.BoolFlag{
|
||||||
Name: "sepolia",
|
Name: "sepolia",
|
||||||
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
Usage: "Sepolia network: pre-configured proof-of-stake test network",
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
HoleskyFlag = &cli.BoolFlag{
|
HoleskyFlag = &cli.BoolFlag{
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,5 @@ the following commands (in this directory) against a synced mainnet node:
|
||||||
```shell
|
```shell
|
||||||
> go run . filtergen --queries queries/filter_queries_mainnet.json http://host:8545
|
> go run . filtergen --queries queries/filter_queries_mainnet.json http://host:8545
|
||||||
> go run . historygen --history-tests queries/history_mainnet.json http://host:8545
|
> go run . historygen --history-tests queries/history_mainnet.json http://host:8545
|
||||||
|
> go run . tracegen --trace-tests queries/trace_mainnet.json http://host:8545
|
||||||
```
|
```
|
||||||
|
|
|
||||||
104
cmd/workload/client.go
Normal file
104
cmd/workload/client.go
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright 2025 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient/gethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
Eth *ethclient.Client
|
||||||
|
Geth *gethclient.Client
|
||||||
|
RPC *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeClient(ctx *cli.Context) *client {
|
||||||
|
if ctx.NArg() < 1 {
|
||||||
|
exit("missing RPC endpoint URL as command-line argument")
|
||||||
|
}
|
||||||
|
url := ctx.Args().First()
|
||||||
|
cl, err := rpc.Dial(url)
|
||||||
|
if err != nil {
|
||||||
|
exit(fmt.Errorf("could not create RPC client at %s: %v", url, err))
|
||||||
|
}
|
||||||
|
return &client{
|
||||||
|
RPC: cl,
|
||||||
|
Eth: ethclient.NewClient(cl),
|
||||||
|
Geth: gethclient.New(cl),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleBlock struct {
|
||||||
|
Number hexutil.Uint64 `json:"number"`
|
||||||
|
Hash common.Hash `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleTransaction struct {
|
||||||
|
Hash common.Hash `json:"hash"`
|
||||||
|
TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getBlockByHash(ctx context.Context, arg common.Hash, fullTx bool) (*simpleBlock, error) {
|
||||||
|
var r *simpleBlock
|
||||||
|
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByHash", arg, fullTx)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getBlockByNumber(ctx context.Context, arg uint64, fullTx bool) (*simpleBlock, error) {
|
||||||
|
var r *simpleBlock
|
||||||
|
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByNumber", hexutil.Uint64(arg), fullTx)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getTransactionByBlockHashAndIndex(ctx context.Context, block common.Hash, index uint64) (*simpleTransaction, error) {
|
||||||
|
var r *simpleTransaction
|
||||||
|
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index))
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getTransactionByBlockNumberAndIndex(ctx context.Context, block uint64, index uint64) (*simpleTransaction, error) {
|
||||||
|
var r *simpleTransaction
|
||||||
|
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockNumberAndIndex", hexutil.Uint64(block), hexutil.Uint64(index))
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getBlockTransactionCountByHash(ctx context.Context, block common.Hash) (uint64, error) {
|
||||||
|
var r hexutil.Uint64
|
||||||
|
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByHash", block)
|
||||||
|
return uint64(r), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getBlockTransactionCountByNumber(ctx context.Context, block uint64) (uint64, error) {
|
||||||
|
var r hexutil.Uint64
|
||||||
|
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByNumber", hexutil.Uint64(block))
|
||||||
|
return uint64(r), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getBlockReceipts(ctx context.Context, arg any) ([]*types.Receipt, error) {
|
||||||
|
var result []*types.Receipt
|
||||||
|
err := c.RPC.CallContext(ctx, &result, "eth_getBlockReceipts", arg)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
@ -45,11 +45,11 @@ func newFilterTestSuite(cfg testConfig) *filterTestSuite {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *filterTestSuite) allTests() []utesting.Test {
|
func (s *filterTestSuite) allTests() []workloadTest {
|
||||||
return []utesting.Test{
|
return []workloadTest{
|
||||||
{Name: "Filter/ShortRange", Fn: s.filterShortRange},
|
newWorkLoadTest("Filter/ShortRange", s.filterShortRange),
|
||||||
{Name: "Filter/LongRange", Fn: s.filterLongRange, Slow: true},
|
newSlowWorkloadTest("Filter/LongRange", s.filterLongRange),
|
||||||
{Name: "Filter/FullRange", Fn: s.filterFullRange, Slow: true},
|
newSlowWorkloadTest("Filter/FullRange", s.filterFullRange),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
|
|
||||||
"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/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -65,40 +64,16 @@ func (s *historyTestSuite) loadTests() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *historyTestSuite) allTests() []utesting.Test {
|
func (s *historyTestSuite) allTests() []workloadTest {
|
||||||
return []utesting.Test{
|
return []workloadTest{
|
||||||
{
|
newWorkLoadTest("History/getBlockByHash", s.testGetBlockByHash),
|
||||||
Name: "History/getBlockByHash",
|
newWorkLoadTest("History/getBlockByNumber", s.testGetBlockByNumber),
|
||||||
Fn: s.testGetBlockByHash,
|
newWorkLoadTest("History/getBlockReceiptsByHash", s.testGetBlockReceiptsByHash),
|
||||||
},
|
newWorkLoadTest("History/getBlockReceiptsByNumber", s.testGetBlockReceiptsByNumber),
|
||||||
{
|
newWorkLoadTest("History/getBlockTransactionCountByHash", s.testGetBlockTransactionCountByHash),
|
||||||
Name: "History/getBlockByNumber",
|
newWorkLoadTest("History/getBlockTransactionCountByNumber", s.testGetBlockTransactionCountByNumber),
|
||||||
Fn: s.testGetBlockByNumber,
|
newWorkLoadTest("History/getTransactionByBlockHashAndIndex", s.testGetTransactionByBlockHashAndIndex),
|
||||||
},
|
newWorkLoadTest("History/getTransactionByBlockNumberAndIndex", s.testGetTransactionByBlockNumberAndIndex),
|
||||||
{
|
|
||||||
Name: "History/getBlockReceiptsByHash",
|
|
||||||
Fn: s.testGetBlockReceiptsByHash,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "History/getBlockReceiptsByNumber",
|
|
||||||
Fn: s.testGetBlockReceiptsByNumber,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "History/getBlockTransactionCountByHash",
|
|
||||||
Fn: s.testGetBlockTransactionCountByHash,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "History/getBlockTransactionCountByNumber",
|
|
||||||
Fn: s.testGetBlockTransactionCountByNumber,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "History/getTransactionByBlockHashAndIndex",
|
|
||||||
Fn: s.testGetTransactionByBlockHashAndIndex,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "History/getTransactionByBlockNumberAndIndex",
|
|
||||||
Fn: s.testGetTransactionByBlockNumberAndIndex,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,55 +254,3 @@ func (s *historyTestSuite) testGetTransactionByBlockNumberAndIndex(t *utesting.T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type simpleBlock struct {
|
|
||||||
Number hexutil.Uint64 `json:"number"`
|
|
||||||
Hash common.Hash `json:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type simpleTransaction struct {
|
|
||||||
Hash common.Hash `json:"hash"`
|
|
||||||
TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getBlockByHash(ctx context.Context, arg common.Hash, fullTx bool) (*simpleBlock, error) {
|
|
||||||
var r *simpleBlock
|
|
||||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByHash", arg, fullTx)
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getBlockByNumber(ctx context.Context, arg uint64, fullTx bool) (*simpleBlock, error) {
|
|
||||||
var r *simpleBlock
|
|
||||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockByNumber", hexutil.Uint64(arg), fullTx)
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getTransactionByBlockHashAndIndex(ctx context.Context, block common.Hash, index uint64) (*simpleTransaction, error) {
|
|
||||||
var r *simpleTransaction
|
|
||||||
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index))
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getTransactionByBlockNumberAndIndex(ctx context.Context, block uint64, index uint64) (*simpleTransaction, error) {
|
|
||||||
var r *simpleTransaction
|
|
||||||
err := c.RPC.CallContext(ctx, &r, "eth_getTransactionByBlockNumberAndIndex", hexutil.Uint64(block), hexutil.Uint64(index))
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getBlockTransactionCountByHash(ctx context.Context, block common.Hash) (uint64, error) {
|
|
||||||
var r hexutil.Uint64
|
|
||||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByHash", block)
|
|
||||||
return uint64(r), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getBlockTransactionCountByNumber(ctx context.Context, block uint64) (uint64, error) {
|
|
||||||
var r hexutil.Uint64
|
|
||||||
err := c.RPC.CallContext(ctx, &r, "eth_getBlockTransactionCountByNumber", hexutil.Uint64(block))
|
|
||||||
return uint64(r), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getBlockReceipts(ctx context.Context, arg any) ([]*types.Receipt, error) {
|
|
||||||
var result []*types.Receipt
|
|
||||||
err := c.RPC.CallContext(ctx, &result, "eth_getBlockReceipts", arg)
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ var (
|
||||||
}
|
}
|
||||||
historyTestEarliestFlag = &cli.IntFlag{
|
historyTestEarliestFlag = &cli.IntFlag{
|
||||||
Name: "earliest",
|
Name: "earliest",
|
||||||
Usage: "JSON file containing filter test queries",
|
Usage: "The earliest block to test queries",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
Category: flags.TestingCategory,
|
Category: flags.TestingCategory,
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ func calcReceiptsHash(rcpt []*types.Receipt) common.Hash {
|
||||||
func writeJSON(fileName string, value any) {
|
func writeJSON(fileName string, value any) {
|
||||||
file, err := os.Create(fileName)
|
file, err := os.Create(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(fmt.Errorf("Error creating %s: %v", fileName, err))
|
exit(fmt.Errorf("error creating %s: %v", fileName, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -49,6 +47,7 @@ func init() {
|
||||||
runTestCommand,
|
runTestCommand,
|
||||||
historyGenerateCommand,
|
historyGenerateCommand,
|
||||||
filterGenerateCommand,
|
filterGenerateCommand,
|
||||||
|
traceGenerateCommand,
|
||||||
filterPerfCommand,
|
filterPerfCommand,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,26 +56,6 @@ func main() {
|
||||||
exit(app.Run(os.Args))
|
exit(app.Run(os.Args))
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
|
||||||
Eth *ethclient.Client
|
|
||||||
RPC *rpc.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeClient(ctx *cli.Context) *client {
|
|
||||||
if ctx.NArg() < 1 {
|
|
||||||
exit("missing RPC endpoint URL as command-line argument")
|
|
||||||
}
|
|
||||||
url := ctx.Args().First()
|
|
||||||
cl, err := rpc.Dial(url)
|
|
||||||
if err != nil {
|
|
||||||
exit(fmt.Errorf("Could not create RPC client at %s: %v", url, err))
|
|
||||||
}
|
|
||||||
return &client{
|
|
||||||
RPC: cl,
|
|
||||||
Eth: ethclient.NewClient(cl),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exit(err any) {
|
func exit(err any) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|
|
||||||
1
cmd/workload/queries/trace_mainnet.json
Normal file
1
cmd/workload/queries/trace_mainnet.json
Normal file
File diff suppressed because one or more lines are too long
1
cmd/workload/queries/trace_sepolia.json
Normal file
1
cmd/workload/queries/trace_sepolia.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/history"
|
"github.com/ethereum/go-ethereum/core/history"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
|
@ -45,10 +44,13 @@ var (
|
||||||
testPatternFlag,
|
testPatternFlag,
|
||||||
testTAPFlag,
|
testTAPFlag,
|
||||||
testSlowFlag,
|
testSlowFlag,
|
||||||
|
testArchiveFlag,
|
||||||
testSepoliaFlag,
|
testSepoliaFlag,
|
||||||
testMainnetFlag,
|
testMainnetFlag,
|
||||||
filterQueryFileFlag,
|
filterQueryFileFlag,
|
||||||
historyTestFileFlag,
|
historyTestFileFlag,
|
||||||
|
traceTestFileFlag,
|
||||||
|
traceTestInvalidOutputFlag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testPatternFlag = &cli.StringFlag{
|
testPatternFlag = &cli.StringFlag{
|
||||||
|
|
@ -67,6 +69,12 @@ var (
|
||||||
Value: false,
|
Value: false,
|
||||||
Category: flags.TestingCategory,
|
Category: flags.TestingCategory,
|
||||||
}
|
}
|
||||||
|
testArchiveFlag = &cli.BoolFlag{
|
||||||
|
Name: "archive",
|
||||||
|
Usage: "Enable archive tests",
|
||||||
|
Value: false,
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
testSepoliaFlag = &cli.BoolFlag{
|
testSepoliaFlag = &cli.BoolFlag{
|
||||||
Name: "sepolia",
|
Name: "sepolia",
|
||||||
Usage: "Use test cases for sepolia network",
|
Usage: "Use test cases for sepolia network",
|
||||||
|
|
@ -86,6 +94,7 @@ type testConfig struct {
|
||||||
filterQueryFile string
|
filterQueryFile string
|
||||||
historyTestFile string
|
historyTestFile string
|
||||||
historyPruneBlock *uint64
|
historyPruneBlock *uint64
|
||||||
|
traceTestFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
var errPrunedHistory = fmt.Errorf("attempt to access pruned history")
|
var errPrunedHistory = fmt.Errorf("attempt to access pruned history")
|
||||||
|
|
@ -125,36 +134,85 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
||||||
cfg.historyTestFile = "queries/history_mainnet.json"
|
cfg.historyTestFile = "queries/history_mainnet.json"
|
||||||
cfg.historyPruneBlock = new(uint64)
|
cfg.historyPruneBlock = new(uint64)
|
||||||
*cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber
|
*cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber
|
||||||
|
cfg.traceTestFile = "queries/trace_mainnet.json"
|
||||||
case ctx.Bool(testSepoliaFlag.Name):
|
case ctx.Bool(testSepoliaFlag.Name):
|
||||||
cfg.fsys = builtinTestFiles
|
cfg.fsys = builtinTestFiles
|
||||||
cfg.filterQueryFile = "queries/filter_queries_sepolia.json"
|
cfg.filterQueryFile = "queries/filter_queries_sepolia.json"
|
||||||
cfg.historyTestFile = "queries/history_sepolia.json"
|
cfg.historyTestFile = "queries/history_sepolia.json"
|
||||||
cfg.historyPruneBlock = new(uint64)
|
cfg.historyPruneBlock = new(uint64)
|
||||||
*cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber
|
*cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber
|
||||||
|
cfg.traceTestFile = "queries/trace_sepolia.json"
|
||||||
default:
|
default:
|
||||||
cfg.fsys = os.DirFS(".")
|
cfg.fsys = os.DirFS(".")
|
||||||
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
||||||
cfg.historyTestFile = ctx.String(historyTestFileFlag.Name)
|
cfg.historyTestFile = ctx.String(historyTestFileFlag.Name)
|
||||||
|
cfg.traceTestFile = ctx.String(traceTestFileFlag.Name)
|
||||||
}
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// workloadTest represents a single test in the workload. It's a wrapper
|
||||||
|
// of utesting.Test by adding a few additional attributes.
|
||||||
|
type workloadTest struct {
|
||||||
|
utesting.Test
|
||||||
|
|
||||||
|
archive bool // Flag whether the archive node (full state history) is required for this test
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWorkLoadTest(name string, fn func(t *utesting.T)) workloadTest {
|
||||||
|
return workloadTest{
|
||||||
|
Test: utesting.Test{
|
||||||
|
Name: name,
|
||||||
|
Fn: fn,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSlowWorkloadTest(name string, fn func(t *utesting.T)) workloadTest {
|
||||||
|
t := newWorkLoadTest(name, fn)
|
||||||
|
t.Slow = true
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArchiveWorkloadTest(name string, fn func(t *utesting.T)) workloadTest {
|
||||||
|
t := newWorkLoadTest(name, fn)
|
||||||
|
t.archive = true
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterTests(tests []workloadTest, pattern string, filterFn func(t workloadTest) bool) []utesting.Test {
|
||||||
|
var utests []utesting.Test
|
||||||
|
for _, t := range tests {
|
||||||
|
if filterFn(t) {
|
||||||
|
utests = append(utests, t.Test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pattern == "" {
|
||||||
|
return utests
|
||||||
|
}
|
||||||
|
return utesting.MatchTests(utests, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
func runTestCmd(ctx *cli.Context) error {
|
func runTestCmd(ctx *cli.Context) error {
|
||||||
cfg := testConfigFromCLI(ctx)
|
cfg := testConfigFromCLI(ctx)
|
||||||
filterSuite := newFilterTestSuite(cfg)
|
filterSuite := newFilterTestSuite(cfg)
|
||||||
historySuite := newHistoryTestSuite(cfg)
|
historySuite := newHistoryTestSuite(cfg)
|
||||||
|
traceSuite := newTraceTestSuite(cfg, ctx)
|
||||||
|
|
||||||
// Filter test cases.
|
// Filter test cases.
|
||||||
tests := filterSuite.allTests()
|
tests := filterSuite.allTests()
|
||||||
tests = append(tests, historySuite.allTests()...)
|
tests = append(tests, historySuite.allTests()...)
|
||||||
if ctx.IsSet(testPatternFlag.Name) {
|
tests = append(tests, traceSuite.allTests()...)
|
||||||
tests = utesting.MatchTests(tests, ctx.String(testPatternFlag.Name))
|
|
||||||
|
utests := filterTests(tests, ctx.String(testPatternFlag.Name), func(t workloadTest) bool {
|
||||||
|
if t.Slow && !ctx.Bool(testSlowFlag.Name) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if !ctx.Bool(testSlowFlag.Name) {
|
if t.archive && !ctx.Bool(testArchiveFlag.Name) {
|
||||||
tests = slices.DeleteFunc(tests, func(test utesting.Test) bool {
|
return false
|
||||||
return test.Slow
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Disable logging unless explicitly enabled.
|
// Disable logging unless explicitly enabled.
|
||||||
if !ctx.IsSet("verbosity") && !ctx.IsSet("vmodule") {
|
if !ctx.IsSet("verbosity") && !ctx.IsSet("vmodule") {
|
||||||
|
|
@ -166,7 +224,7 @@ func runTestCmd(ctx *cli.Context) error {
|
||||||
if ctx.Bool(testTAPFlag.Name) {
|
if ctx.Bool(testTAPFlag.Name) {
|
||||||
run = utesting.RunTAP
|
run = utesting.RunTAP
|
||||||
}
|
}
|
||||||
results := run(tests, os.Stdout)
|
results := run(utests, os.Stdout)
|
||||||
if utesting.CountFailures(results) > 0 {
|
if utesting.CountFailures(results) > 0 {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
126
cmd/workload/tracetest.go
Normal file
126
cmd/workload/tracetest.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright 2025 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// traceTest is the content of a history test.
|
||||||
|
type traceTest struct {
|
||||||
|
TxHashes []common.Hash `json:"txHashes"`
|
||||||
|
TraceConfigs []tracers.TraceConfig `json:"traceConfigs"`
|
||||||
|
ResultHashes []common.Hash `json:"resultHashes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type traceTestSuite struct {
|
||||||
|
cfg testConfig
|
||||||
|
tests traceTest
|
||||||
|
invalidDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTraceTestSuite(cfg testConfig, ctx *cli.Context) *traceTestSuite {
|
||||||
|
s := &traceTestSuite{
|
||||||
|
cfg: cfg,
|
||||||
|
invalidDir: ctx.String(traceTestInvalidOutputFlag.Name),
|
||||||
|
}
|
||||||
|
if err := s.loadTests(); err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *traceTestSuite) loadTests() error {
|
||||||
|
file, err := s.cfg.fsys.Open(s.cfg.traceTestFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't open traceTestFile: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if err := json.NewDecoder(file).Decode(&s.tests); err != nil {
|
||||||
|
return fmt.Errorf("invalid JSON in %s: %v", s.cfg.traceTestFile, err)
|
||||||
|
}
|
||||||
|
if len(s.tests.TxHashes) == 0 {
|
||||||
|
return fmt.Errorf("traceTestFile %s has no test data", s.cfg.traceTestFile)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *traceTestSuite) allTests() []workloadTest {
|
||||||
|
return []workloadTest{
|
||||||
|
newArchiveWorkloadTest("Trace/Transaction", s.traceTransaction),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// traceTransaction runs all transaction tracing tests
|
||||||
|
func (s *traceTestSuite) traceTransaction(t *utesting.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
for i, hash := range s.tests.TxHashes {
|
||||||
|
config := s.tests.TraceConfigs[i]
|
||||||
|
result, err := s.cfg.client.Geth.TraceTransaction(ctx, hash, &config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Transaction %d (hash %v): error %v", i, hash, err)
|
||||||
|
}
|
||||||
|
blob, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Transaction %d (hash %v): error %v", i, hash, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if crypto.Keccak256Hash(blob) != s.tests.ResultHashes[i] {
|
||||||
|
t.Errorf("Transaction %d (hash %v): invalid result", i, hash)
|
||||||
|
|
||||||
|
writeInvalidTraceResult(s.invalidDir, hash, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeInvalidTraceResult(dir string, hash common.Hash, result any) {
|
||||||
|
if dir == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := os.MkdirAll(dir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to make output directory", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := filepath.Join(dir, "invalid"+"_"+hash.String())
|
||||||
|
file, err := os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
exit(fmt.Errorf("error creating %s: %v", name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
data, _ := json.MarshalIndent(result, "", " ")
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
exit(fmt.Errorf("error writing %s: %v", name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
195
cmd/workload/tracetestgen.go
Normal file
195
cmd/workload/tracetestgen.go
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
// Copyright 2025 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultBlocksToTrace = 64 // the number of states assumed to be available
|
||||||
|
|
||||||
|
traceGenerateCommand = &cli.Command{
|
||||||
|
Name: "tracegen",
|
||||||
|
Usage: "Generates tests for state tracing",
|
||||||
|
ArgsUsage: "<RPC endpoint URL>",
|
||||||
|
Action: generateTraceTests,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
traceTestFileFlag,
|
||||||
|
traceTestResultOutputFlag,
|
||||||
|
traceTestBlockFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
traceTestFileFlag = &cli.StringFlag{
|
||||||
|
Name: "trace-tests",
|
||||||
|
Usage: "JSON file containing trace test queries",
|
||||||
|
Value: "trace_tests.json",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
traceTestResultOutputFlag = &cli.StringFlag{
|
||||||
|
Name: "trace-output",
|
||||||
|
Usage: "Folder containing the trace output files",
|
||||||
|
Value: "",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
traceTestBlockFlag = &cli.IntFlag{
|
||||||
|
Name: "trace-blocks",
|
||||||
|
Usage: "The number of blocks for tracing",
|
||||||
|
Value: defaultBlocksToTrace,
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
traceTestInvalidOutputFlag = &cli.StringFlag{
|
||||||
|
Name: "trace-invalid",
|
||||||
|
Usage: "Folder containing the mismatched trace output files",
|
||||||
|
Value: "",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateTraceTests(clictx *cli.Context) error {
|
||||||
|
var (
|
||||||
|
client = makeClient(clictx)
|
||||||
|
outputFile = clictx.String(traceTestFileFlag.Name)
|
||||||
|
outputDir = clictx.String(traceTestResultOutputFlag.Name)
|
||||||
|
blocks = clictx.Int(traceTestBlockFlag.Name)
|
||||||
|
ctx = context.Background()
|
||||||
|
test = new(traceTest)
|
||||||
|
)
|
||||||
|
if outputDir != "" {
|
||||||
|
err := os.MkdirAll(outputDir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
latest, err := client.Eth.BlockNumber(ctx)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
if latest < uint64(blocks) {
|
||||||
|
exit(fmt.Errorf("node seems not synced, latest block is %d", latest))
|
||||||
|
}
|
||||||
|
// Get blocks and assign block info into the test
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
logged = time.Now()
|
||||||
|
failed int
|
||||||
|
)
|
||||||
|
log.Info("Trace transactions around the chain tip", "head", latest, "blocks", blocks)
|
||||||
|
|
||||||
|
for i := 0; i < blocks; i++ {
|
||||||
|
number := latest - uint64(i)
|
||||||
|
block, err := client.Eth.BlockByNumber(ctx, big.NewInt(int64(number)))
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
for _, tx := range block.Transactions() {
|
||||||
|
config, configName := randomTraceOption()
|
||||||
|
result, err := client.Geth.TraceTransaction(ctx, tx.Hash(), config)
|
||||||
|
if err != nil {
|
||||||
|
failed += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
blob, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
failed += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
test.TxHashes = append(test.TxHashes, tx.Hash())
|
||||||
|
test.TraceConfigs = append(test.TraceConfigs, *config)
|
||||||
|
test.ResultHashes = append(test.ResultHashes, crypto.Keccak256Hash(blob))
|
||||||
|
writeTraceResult(outputDir, tx.Hash(), result, configName)
|
||||||
|
}
|
||||||
|
if time.Since(logged) > time.Second*8 {
|
||||||
|
logged = time.Now()
|
||||||
|
log.Info("Tracing transactions", "executed", len(test.TxHashes), "failed", failed, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("Traced transactions", "executed", len(test.TxHashes), "failed", failed, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
|
||||||
|
// Write output file.
|
||||||
|
writeJSON(outputFile, test)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomTraceOption() (*tracers.TraceConfig, string) {
|
||||||
|
x := rand.Intn(11)
|
||||||
|
if x == 0 {
|
||||||
|
// struct-logger, with all fields enabled, very heavy
|
||||||
|
return &tracers.TraceConfig{
|
||||||
|
Config: &logger.Config{
|
||||||
|
EnableMemory: true,
|
||||||
|
EnableReturnData: true,
|
||||||
|
},
|
||||||
|
}, "structAll"
|
||||||
|
}
|
||||||
|
if x == 1 {
|
||||||
|
// default options for struct-logger, with stack and storage capture
|
||||||
|
// enabled
|
||||||
|
return &tracers.TraceConfig{
|
||||||
|
Config: &logger.Config{},
|
||||||
|
}, "structDefault"
|
||||||
|
}
|
||||||
|
if x == 2 || x == 3 || x == 4 {
|
||||||
|
// struct-logger with storage capture enabled
|
||||||
|
return &tracers.TraceConfig{
|
||||||
|
Config: &logger.Config{
|
||||||
|
DisableStack: true,
|
||||||
|
},
|
||||||
|
}, "structStorage"
|
||||||
|
}
|
||||||
|
// Native tracer
|
||||||
|
loggers := []string{"callTracer", "4byteTracer", "flatCallTracer", "muxTracer", "noopTracer", "prestateTracer"}
|
||||||
|
return &tracers.TraceConfig{
|
||||||
|
Tracer: &loggers[x-5],
|
||||||
|
}, loggers[x-5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTraceResult(dir string, hash common.Hash, result any, configName string) {
|
||||||
|
if dir == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := filepath.Join(dir, configName+"_"+hash.String())
|
||||||
|
file, err := os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
exit(fmt.Errorf("error creating %s: %v", name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
data, _ := json.MarshalIndent(result, "", " ")
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
exit(fmt.Errorf("error writing %s: %v", name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -66,7 +66,7 @@ var (
|
||||||
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
|
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
|
||||||
|
|
||||||
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
||||||
chainMgaspsGauge = metrics.NewRegisteredGauge("chain/mgasps", nil)
|
chainMgaspsMeter = metrics.NewRegisteredResettingTimer("chain/mgasps", nil)
|
||||||
|
|
||||||
accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil)
|
accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil)
|
||||||
accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil)
|
accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil)
|
||||||
|
|
@ -278,7 +278,6 @@ type BlockChain struct {
|
||||||
txLookupLock sync.RWMutex
|
txLookupLock sync.RWMutex
|
||||||
txLookupCache *lru.Cache[common.Hash, txLookup]
|
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||||
|
|
||||||
quit chan struct{} // shutdown signal, closed in Stop.
|
|
||||||
stopping atomic.Bool // false if chain is running, true when stopped
|
stopping atomic.Bool // false if chain is running, true when stopped
|
||||||
procInterrupt atomic.Bool // interrupt signaler for block processing
|
procInterrupt atomic.Bool // interrupt signaler for block processing
|
||||||
|
|
||||||
|
|
@ -328,7 +327,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
db: db,
|
db: db,
|
||||||
triedb: triedb,
|
triedb: triedb,
|
||||||
triegc: prque.New[int64, common.Hash](nil),
|
triegc: prque.New[int64, common.Hash](nil),
|
||||||
quit: make(chan struct{}),
|
|
||||||
chainmu: syncx.NewClosableMutex(),
|
chainmu: syncx.NewClosableMutex(),
|
||||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
||||||
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
||||||
|
|
@ -1206,7 +1204,6 @@ func (bc *BlockChain) stopWithoutSaving() {
|
||||||
bc.scope.Close()
|
bc.scope.Close()
|
||||||
|
|
||||||
// Signal shutdown to all goroutines.
|
// Signal shutdown to all goroutines.
|
||||||
close(bc.quit)
|
|
||||||
bc.StopInsert()
|
bc.StopInsert()
|
||||||
|
|
||||||
// Now wait for all chain modifications to end and persistent goroutines to exit.
|
// Now wait for all chain modifications to end and persistent goroutines to exit.
|
||||||
|
|
@ -2067,7 +2064,12 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
|
||||||
|
|
||||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||||
blockInsertTimer.UpdateSince(startTime)
|
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||||
|
blockInsertTimer.Update(elapsed)
|
||||||
|
|
||||||
|
// TODO(rjl493456442) generalize the ResettingTimer
|
||||||
|
mgasps := float64(res.GasUsed) * 1000 / float64(elapsed)
|
||||||
|
chainMgaspsMeter.Update(time.Duration(mgasps))
|
||||||
|
|
||||||
return &blockProcessingResult{
|
return &blockProcessingResult{
|
||||||
usedGas: res.GasUsed,
|
usedGas: res.GasUsed,
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,6 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
||||||
mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
|
mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
|
||||||
)
|
)
|
||||||
// Update the Mgas per second gauge
|
|
||||||
chainMgaspsGauge.Update(int64(mgasps))
|
|
||||||
|
|
||||||
// If we're at the last block of the batch or report period reached, log
|
// If we're at the last block of the batch or report period reached, log
|
||||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||||
// Count the number of transactions in this segment
|
// Count the number of transactions in this segment
|
||||||
|
|
|
||||||
|
|
@ -684,6 +684,7 @@ type fullLogRLP struct {
|
||||||
Topics []common.Hash
|
Topics []common.Hash
|
||||||
Data []byte
|
Data []byte
|
||||||
BlockNumber uint64
|
BlockNumber uint64
|
||||||
|
BlockTimestamp uint64
|
||||||
TxHash common.Hash
|
TxHash common.Hash
|
||||||
TxIndex uint
|
TxIndex uint
|
||||||
BlockHash common.Hash
|
BlockHash common.Hash
|
||||||
|
|
@ -696,6 +697,7 @@ func newFullLogRLP(l *types.Log) *fullLogRLP {
|
||||||
Topics: l.Topics,
|
Topics: l.Topics,
|
||||||
Data: l.Data,
|
Data: l.Data,
|
||||||
BlockNumber: l.BlockNumber,
|
BlockNumber: l.BlockNumber,
|
||||||
|
BlockTimestamp: l.BlockTimestamp,
|
||||||
TxHash: l.TxHash,
|
TxHash: l.TxHash,
|
||||||
TxIndex: l.TxIndex,
|
TxIndex: l.TxIndex,
|
||||||
BlockHash: l.BlockHash,
|
BlockHash: l.BlockHash,
|
||||||
|
|
@ -834,7 +836,7 @@ func TestDeriveLogFields(t *testing.T) {
|
||||||
// Derive log metadata fields
|
// Derive log metadata fields
|
||||||
number := big.NewInt(1)
|
number := big.NewInt(1)
|
||||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||||
types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, number.Uint64(), 0, big.NewInt(0), big.NewInt(0), txs)
|
types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, number.Uint64(), 12, big.NewInt(0), big.NewInt(0), txs)
|
||||||
|
|
||||||
// Iterate over all the computed fields and check that they're correct
|
// Iterate over all the computed fields and check that they're correct
|
||||||
logIndex := uint(0)
|
logIndex := uint(0)
|
||||||
|
|
@ -846,6 +848,9 @@ func TestDeriveLogFields(t *testing.T) {
|
||||||
if receipts[i].Logs[j].BlockHash != hash {
|
if receipts[i].Logs[j].BlockHash != hash {
|
||||||
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
|
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
|
||||||
}
|
}
|
||||||
|
if receipts[i].Logs[j].BlockTimestamp != 12 {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].BlockTimestamp = %d, want %d", i, j, receipts[i].Logs[j].BlockTimestamp, 12)
|
||||||
|
}
|
||||||
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
|
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
|
||||||
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
|
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ 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/ethereum/go-ethereum/crypto"
|
||||||
"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"
|
||||||
|
|
@ -128,6 +129,46 @@ func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func(common.Hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findTxInBlockBody traverses the given RLP-encoded block body, searching for
|
||||||
|
// the transaction specified by its hash.
|
||||||
|
func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Transaction, uint64, error) {
|
||||||
|
txnListRLP, _, err := rlp.SplitList(blockbody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
iter, err := rlp.NewListIterator(txnListRLP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
txIndex := uint64(0)
|
||||||
|
for iter.Next() {
|
||||||
|
if iter.Err() != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
// The preimage for the hash calculation of legacy transactions
|
||||||
|
// is just their RLP encoding. For typed (EIP-2718) transactions,
|
||||||
|
// which are encoded as byte arrays, the preimage is the content of
|
||||||
|
// the byte array, so trim their prefix here.
|
||||||
|
txRLP := iter.Value()
|
||||||
|
kind, txHashPayload, _, err := rlp.Split(txRLP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if kind == rlp.List { // Legacy transaction
|
||||||
|
txHashPayload = txRLP
|
||||||
|
}
|
||||||
|
if crypto.Keccak256Hash(txHashPayload) == target {
|
||||||
|
var tx types.Transaction
|
||||||
|
if err := rlp.DecodeBytes(txRLP, &tx); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
return &tx, txIndex, nil
|
||||||
|
}
|
||||||
|
txIndex++
|
||||||
|
}
|
||||||
|
return nil, 0, errors.New("transaction not found")
|
||||||
|
}
|
||||||
|
|
||||||
// ReadTransaction retrieves a specific transaction from the database, along with
|
// ReadTransaction retrieves a specific transaction from the database, along with
|
||||||
// its added positional metadata.
|
// its added positional metadata.
|
||||||
func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
||||||
|
|
@ -139,18 +180,17 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com
|
||||||
if blockHash == (common.Hash{}) {
|
if blockHash == (common.Hash{}) {
|
||||||
return nil, common.Hash{}, 0, 0
|
return nil, common.Hash{}, 0, 0
|
||||||
}
|
}
|
||||||
body := ReadBody(db, blockHash, *blockNumber)
|
bodyRLP := ReadBodyRLP(db, blockHash, *blockNumber)
|
||||||
if body == nil {
|
if bodyRLP == nil {
|
||||||
log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
|
log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
|
||||||
return nil, common.Hash{}, 0, 0
|
return nil, common.Hash{}, 0, 0
|
||||||
}
|
}
|
||||||
for txIndex, tx := range body.Transactions {
|
tx, txIndex, err := findTxInBlockBody(bodyRLP, hash)
|
||||||
if tx.Hash() == hash {
|
if err != nil {
|
||||||
return tx, blockHash, *blockNumber, uint64(txIndex)
|
log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash, "err", err)
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
|
|
||||||
return nil, common.Hash{}, 0, 0
|
return nil, common.Hash{}, 0, 0
|
||||||
|
}
|
||||||
|
return tx, blockHash, *blockNumber, txIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,13 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"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/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/blocktest"
|
"github.com/ethereum/go-ethereum/internal/blocktest"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var newTestHasher = blocktest.NewHasher
|
var newTestHasher = blocktest.NewHasher
|
||||||
|
|
@ -72,7 +74,15 @@ func TestLookupStorage(t *testing.T) {
|
||||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
||||||
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
|
||||||
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
|
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
|
||||||
txs := []*types.Transaction{tx1, tx2, tx3}
|
tx4 := types.NewTx(&types.DynamicFeeTx{
|
||||||
|
To: new(common.Address),
|
||||||
|
Nonce: 5,
|
||||||
|
Value: big.NewInt(5),
|
||||||
|
Gas: 5,
|
||||||
|
GasTipCap: big.NewInt(55),
|
||||||
|
GasFeeCap: big.NewInt(1055),
|
||||||
|
})
|
||||||
|
txs := []*types.Transaction{tx1, tx2, tx3, tx4}
|
||||||
|
|
||||||
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher())
|
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher())
|
||||||
|
|
||||||
|
|
@ -109,3 +119,103 @@ func TestLookupStorage(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFindTxInBlockBody(t *testing.T) {
|
||||||
|
tx1 := types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 1,
|
||||||
|
GasPrice: big.NewInt(1),
|
||||||
|
Gas: 1,
|
||||||
|
To: new(common.Address),
|
||||||
|
Value: big.NewInt(5),
|
||||||
|
Data: []byte{0x11, 0x11, 0x11},
|
||||||
|
})
|
||||||
|
tx2 := types.NewTx(&types.AccessListTx{
|
||||||
|
Nonce: 1,
|
||||||
|
GasPrice: big.NewInt(1),
|
||||||
|
Gas: 1,
|
||||||
|
To: new(common.Address),
|
||||||
|
Value: big.NewInt(5),
|
||||||
|
Data: []byte{0x11, 0x11, 0x11},
|
||||||
|
AccessList: []types.AccessTuple{
|
||||||
|
{
|
||||||
|
Address: common.Address{0x1},
|
||||||
|
StorageKeys: []common.Hash{{0x1}, {0x2}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tx3 := types.NewTx(&types.DynamicFeeTx{
|
||||||
|
Nonce: 1,
|
||||||
|
Gas: 1,
|
||||||
|
To: new(common.Address),
|
||||||
|
Value: big.NewInt(5),
|
||||||
|
Data: []byte{0x11, 0x11, 0x11},
|
||||||
|
GasTipCap: big.NewInt(55),
|
||||||
|
GasFeeCap: big.NewInt(1055),
|
||||||
|
AccessList: []types.AccessTuple{
|
||||||
|
{
|
||||||
|
Address: common.Address{0x1},
|
||||||
|
StorageKeys: []common.Hash{{0x1}, {0x2}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tx4 := types.NewTx(&types.BlobTx{
|
||||||
|
Nonce: 1,
|
||||||
|
Gas: 1,
|
||||||
|
To: common.Address{0x1},
|
||||||
|
Value: uint256.NewInt(5),
|
||||||
|
Data: []byte{0x11, 0x11, 0x11},
|
||||||
|
GasTipCap: uint256.NewInt(55),
|
||||||
|
GasFeeCap: uint256.NewInt(1055),
|
||||||
|
AccessList: []types.AccessTuple{
|
||||||
|
{
|
||||||
|
Address: common.Address{0x1},
|
||||||
|
StorageKeys: []common.Hash{{0x1}, {0x2}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BlobFeeCap: uint256.NewInt(1),
|
||||||
|
BlobHashes: []common.Hash{{0x1}, {0x2}},
|
||||||
|
})
|
||||||
|
tx5 := types.NewTx(&types.SetCodeTx{
|
||||||
|
Nonce: 1,
|
||||||
|
Gas: 1,
|
||||||
|
To: common.Address{0x1},
|
||||||
|
Value: uint256.NewInt(5),
|
||||||
|
Data: []byte{0x11, 0x11, 0x11},
|
||||||
|
GasTipCap: uint256.NewInt(55),
|
||||||
|
GasFeeCap: uint256.NewInt(1055),
|
||||||
|
AccessList: []types.AccessTuple{
|
||||||
|
{
|
||||||
|
Address: common.Address{0x1},
|
||||||
|
StorageKeys: []common.Hash{{0x1}, {0x2}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AuthList: []types.SetCodeAuthorization{
|
||||||
|
{
|
||||||
|
ChainID: uint256.Int{1},
|
||||||
|
Address: common.Address{0x1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
txs := []*types.Transaction{tx1, tx2, tx3, tx4, tx5}
|
||||||
|
|
||||||
|
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher())
|
||||||
|
db := NewMemoryDatabase()
|
||||||
|
WriteBlock(db, block)
|
||||||
|
|
||||||
|
rlp := ReadBodyRLP(db, block.Hash(), block.NumberU64())
|
||||||
|
for i := 0; i < len(txs); i++ {
|
||||||
|
tx, txIndex, err := findTxInBlockBody(rlp, txs[i].Hash())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to retrieve tx, err: %v", err)
|
||||||
|
}
|
||||||
|
if txIndex != uint64(i) {
|
||||||
|
t.Fatalf("Unexpected transaction index, want: %d, got: %d", i, txIndex)
|
||||||
|
}
|
||||||
|
if tx.Hash() != txs[i].Hash() {
|
||||||
|
want := spew.Sdump(txs[i])
|
||||||
|
got := spew.Sdump(tx)
|
||||||
|
t.Fatalf("Unexpected transaction, want: %s, got: %s", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -667,7 +667,6 @@ func SafeDeleteRange(db ethdb.KeyValueStore, start, end []byte, hashScheme bool,
|
||||||
|
|
||||||
var (
|
var (
|
||||||
count, deleted, skipped int
|
count, deleted, skipped int
|
||||||
buff = crypto.NewKeccakState()
|
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -680,7 +679,7 @@ func SafeDeleteRange(db ethdb.KeyValueStore, start, end []byte, hashScheme bool,
|
||||||
|
|
||||||
for it.Next() && bytes.Compare(end, it.Key()) > 0 {
|
for it.Next() && bytes.Compare(end, it.Key()) > 0 {
|
||||||
// Prevent deletion for trie nodes in hash mode
|
// Prevent deletion for trie nodes in hash mode
|
||||||
if len(it.Key()) != 32 || crypto.HashData(buff, it.Value()) != common.BytesToHash(it.Key()) {
|
if len(it.Key()) != 32 || crypto.Keccak256Hash(it.Value()) != common.BytesToHash(it.Key()) {
|
||||||
if err := batch.Delete(it.Key()); err != nil {
|
if err := batch.Delete(it.Key()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2017 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 rawdb
|
|
||||||
|
|
@ -182,7 +182,11 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
||||||
log.Error("Failed to decode the value returned by iterator", "error", err)
|
log.Error("Failed to decode the value returned by iterator", "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
|
key := s.trie.GetKey(storageIt.Key)
|
||||||
|
if key == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
account.Storage[common.BytesToHash(key)] = common.Bytes2Hex(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.OnAccount(address, account)
|
c.OnAccount(address, account)
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,6 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
type trieReader struct {
|
type trieReader struct {
|
||||||
root common.Hash // State root which uniquely represent a state
|
root common.Hash // State root which uniquely represent a state
|
||||||
db *triedb.Database // Database for loading trie
|
db *triedb.Database // Database for loading trie
|
||||||
buff crypto.KeccakState // Buffer for keccak256 hashing
|
|
||||||
|
|
||||||
// Main trie, resolved in constructor. Note either the Merkle-Patricia-tree
|
// Main trie, resolved in constructor. Note either the Merkle-Patricia-tree
|
||||||
// or Verkle-tree is not safe for concurrent read.
|
// or Verkle-tree is not safe for concurrent read.
|
||||||
|
|
@ -235,7 +234,6 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
|
||||||
return &trieReader{
|
return &trieReader{
|
||||||
root: root,
|
root: root,
|
||||||
db: db,
|
db: db,
|
||||||
buff: crypto.NewKeccakState(),
|
|
||||||
mainTrie: tr,
|
mainTrie: tr,
|
||||||
subRoots: make(map[common.Address]common.Hash),
|
subRoots: make(map[common.Address]common.Hash),
|
||||||
subTries: make(map[common.Address]Trie),
|
subTries: make(map[common.Address]Trie),
|
||||||
|
|
@ -298,7 +296,7 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
root = r.subRoots[addr]
|
root = r.subRoots[addr]
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.buff, addr.Bytes()), root), r.db)
|
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.Keccak256Hash(addr.Bytes()), root), r.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,6 @@ func (s *stateObject) updateRoot() {
|
||||||
// fulfills the storage diffs into the given accountUpdate struct.
|
// fulfills the storage diffs into the given accountUpdate struct.
|
||||||
func (s *stateObject) commitStorage(op *accountUpdate) {
|
func (s *stateObject) commitStorage(op *accountUpdate) {
|
||||||
var (
|
var (
|
||||||
buf = crypto.NewKeccakState()
|
|
||||||
encode = func(val common.Hash) []byte {
|
encode = func(val common.Hash) []byte {
|
||||||
if val == (common.Hash{}) {
|
if val == (common.Hash{}) {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -394,7 +393,7 @@ func (s *stateObject) commitStorage(op *accountUpdate) {
|
||||||
if val == s.originStorage[key] {
|
if val == s.originStorage[key] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hash := crypto.HashData(buf, key[:])
|
hash := crypto.Keccak256Hash(key[:])
|
||||||
if op.storages == nil {
|
if op.storages == nil {
|
||||||
op.storages = make(map[common.Hash][]byte)
|
op.storages = make(map[common.Hash][]byte)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,11 +252,12 @@ func (s *StateDB) AddLog(log *types.Log) {
|
||||||
|
|
||||||
// GetLogs returns the logs matching the specified transaction hash, and annotates
|
// GetLogs returns the logs matching the specified transaction hash, and annotates
|
||||||
// them with the given blockNumber and blockHash.
|
// them with the given blockNumber and blockHash.
|
||||||
func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log {
|
func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash, blockTime uint64) []*types.Log {
|
||||||
logs := s.logs[hash]
|
logs := s.logs[hash]
|
||||||
for _, l := range logs {
|
for _, l := range logs {
|
||||||
l.BlockNumber = blockNumber
|
l.BlockNumber = blockNumber
|
||||||
l.BlockHash = blockHash
|
l.BlockHash = blockHash
|
||||||
|
l.BlockTimestamp = blockTime
|
||||||
}
|
}
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
@ -1063,7 +1064,6 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root
|
||||||
func (s *StateDB) handleDestruction(noStorageWiping bool) (map[common.Hash]*accountDelete, []*trienode.NodeSet, error) {
|
func (s *StateDB) handleDestruction(noStorageWiping bool) (map[common.Hash]*accountDelete, []*trienode.NodeSet, error) {
|
||||||
var (
|
var (
|
||||||
nodes []*trienode.NodeSet
|
nodes []*trienode.NodeSet
|
||||||
buf = crypto.NewKeccakState()
|
|
||||||
deletes = make(map[common.Hash]*accountDelete)
|
deletes = make(map[common.Hash]*accountDelete)
|
||||||
)
|
)
|
||||||
for addr, prevObj := range s.stateObjectsDestruct {
|
for addr, prevObj := range s.stateObjectsDestruct {
|
||||||
|
|
@ -1078,7 +1078,7 @@ func (s *StateDB) handleDestruction(noStorageWiping bool) (map[common.Hash]*acco
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// The account was existent, it can be either case (c) or (d).
|
// The account was existent, it can be either case (c) or (d).
|
||||||
addrHash := crypto.HashData(buf, addr.Bytes())
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||||
op := &accountDelete{
|
op := &accountDelete{
|
||||||
address: addr,
|
address: addr,
|
||||||
origin: types.SlimAccountRLP(*prev),
|
origin: types.SlimAccountRLP(*prev),
|
||||||
|
|
|
||||||
|
|
@ -669,9 +669,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||||
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
|
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
|
||||||
state.GetRefund(), checkstate.GetRefund())
|
state.GetRefund(), checkstate.GetRefund())
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) {
|
if !reflect.DeepEqual(state.GetLogs(common.Hash{}, 0, common.Hash{}, 0), checkstate.GetLogs(common.Hash{}, 0, common.Hash{}, 0)) {
|
||||||
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
|
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
|
||||||
state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{}))
|
state.GetLogs(common.Hash{}, 0, common.Hash{}, 0), checkstate.GetLogs(common.Hash{}, 0, common.Hash{}, 0))
|
||||||
}
|
}
|
||||||
if !maps.Equal(state.journal.dirties, checkstate.journal.dirties) {
|
if !maps.Equal(state.journal.dirties, checkstate.journal.dirties) {
|
||||||
getKeys := func(dirty map[common.Address]int) string {
|
getKeys := func(dirty map[common.Address]int) string {
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm)
|
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +136,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
// this method takes an already created EVM instance as input.
|
// this method takes an already created EVM instance as input.
|
||||||
func ApplyTransactionWithEVM(msg *Message, 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, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
||||||
if hooks := evm.Config.Tracer; hooks != nil {
|
if hooks := evm.Config.Tracer; hooks != nil {
|
||||||
if hooks.OnTxStart != nil {
|
if hooks.OnTxStart != nil {
|
||||||
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
|
@ -165,11 +165,11 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
|
||||||
statedb.AccessEvents().Merge(evm.AccessEvents)
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil
|
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
||||||
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
// by the tx.
|
// by the tx.
|
||||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
||||||
|
|
@ -192,7 +192,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash, blockTime)
|
||||||
receipt.Bloom = types.CreateBloom(receipt)
|
receipt.Bloom = types.CreateBloom(receipt)
|
||||||
receipt.BlockHash = blockHash
|
receipt.BlockHash = blockHash
|
||||||
receipt.BlockNumber = blockNumber
|
receipt.BlockNumber = blockNumber
|
||||||
|
|
@ -210,7 +210,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create a new context to be used in the EVM environment
|
// Create a new context to be used in the EVM environment
|
||||||
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm)
|
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, usedGas, evm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||||
|
|
|
||||||
|
|
@ -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/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
|
||||||
"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/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
@ -1302,27 +1301,13 @@ func (p *BlobPool) GetMetadata(hash common.Hash) *txpool.TxMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlobs returns a number of blobs are proofs for the given versioned hashes.
|
// GetBlobs returns a number of blobs and proofs for the given versioned hashes.
|
||||||
// This is a utility method for the engine API, enabling consensus clients to
|
// This is a utility method for the engine API, enabling consensus clients to
|
||||||
// retrieve blobs from the pools directly instead of the network.
|
// retrieve blobs from the pools directly instead of the network.
|
||||||
func (p *BlobPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) {
|
func (p *BlobPool) GetBlobs(vhashes []common.Hash) []*types.BlobTxSidecar {
|
||||||
// Create a map of the blob hash to indices for faster fills
|
sidecars := make([]*types.BlobTxSidecar, len(vhashes))
|
||||||
var (
|
for idx, vhash := range vhashes {
|
||||||
blobs = make([]*kzg4844.Blob, len(vhashes))
|
// Retrieve the datastore item (in a short lock)
|
||||||
proofs = make([]*kzg4844.Proof, len(vhashes))
|
|
||||||
)
|
|
||||||
index := make(map[common.Hash]int)
|
|
||||||
for i, vhash := range vhashes {
|
|
||||||
index[vhash] = i
|
|
||||||
}
|
|
||||||
// Iterate over the blob hashes, pulling transactions that fill it. Take care
|
|
||||||
// to also fill anything else the transaction might include (probably will).
|
|
||||||
for i, vhash := range vhashes {
|
|
||||||
// If already filled by a previous fetch, skip
|
|
||||||
if blobs[i] != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Unfilled, retrieve the datastore item (in a short lock)
|
|
||||||
p.lock.RLock()
|
p.lock.RLock()
|
||||||
id, exists := p.lookup.storeidOfBlob(vhash)
|
id, exists := p.lookup.storeidOfBlob(vhash)
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|
@ -1342,16 +1327,24 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.
|
||||||
log.Error("Blobs corrupted for traced transaction", "id", id, "err", err)
|
log.Error("Blobs corrupted for traced transaction", "id", id, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Fill anything requested, not just the current versioned hash
|
sidecars[idx] = item.BlobTxSidecar()
|
||||||
sidecar := item.BlobTxSidecar()
|
}
|
||||||
for j, blobhash := range item.BlobHashes() {
|
return sidecars
|
||||||
if idx, ok := index[blobhash]; ok {
|
}
|
||||||
blobs[idx] = &sidecar.Blobs[j]
|
|
||||||
proofs[idx] = &sidecar.Proofs[j]
|
// AvailableBlobs returns the number of blobs that are available in the subpool.
|
||||||
|
func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
|
||||||
|
available := 0
|
||||||
|
for _, vhash := range vhashes {
|
||||||
|
// Retrieve the datastore item (in a short lock)
|
||||||
|
p.lock.RLock()
|
||||||
|
_, exists := p.lookup.storeidOfBlob(vhash)
|
||||||
|
p.lock.RUnlock()
|
||||||
|
if exists {
|
||||||
|
available++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return available
|
||||||
return blobs, proofs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add inserts a set of blob transactions into the pool if they pass validation (both
|
// Add inserts a set of blob transactions into the pool if they pass validation (both
|
||||||
|
|
|
||||||
|
|
@ -417,8 +417,23 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) {
|
||||||
for i := range testBlobVHashes {
|
for i := range testBlobVHashes {
|
||||||
copy(hashes[i][:], testBlobVHashes[i][:])
|
copy(hashes[i][:], testBlobVHashes[i][:])
|
||||||
}
|
}
|
||||||
blobs, proofs := pool.GetBlobs(hashes)
|
sidecars := pool.GetBlobs(hashes)
|
||||||
|
var blobs []*kzg4844.Blob
|
||||||
|
var proofs []*kzg4844.Proof
|
||||||
|
for idx, sidecar := range sidecars {
|
||||||
|
if sidecar == nil {
|
||||||
|
blobs = append(blobs, nil)
|
||||||
|
proofs = append(proofs, nil)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
blobHashes := sidecar.BlobHashes()
|
||||||
|
for i, hash := range blobHashes {
|
||||||
|
if hash == hashes[idx] {
|
||||||
|
blobs = append(blobs, &sidecar.Blobs[i])
|
||||||
|
proofs = append(proofs, &sidecar.Proofs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Cross validate what we received vs what we wanted
|
// Cross validate what we received vs what we wanted
|
||||||
if len(blobs) != len(hashes) || len(proofs) != len(hashes) {
|
if len(blobs) != len(hashes) || len(proofs) != len(hashes) {
|
||||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), len(hashes))
|
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), len(hashes))
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
|
||||||
"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/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
@ -1063,12 +1062,6 @@ func (pool *LegacyPool) GetMetadata(hash common.Hash) *txpool.TxMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlobs is not supported by the legacy transaction pool, it is just here to
|
|
||||||
// implement the txpool.SubPool interface.
|
|
||||||
func (pool *LegacyPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has returns an indicator whether txpool has a transaction cached with the
|
// Has returns an indicator whether txpool has a transaction cached with the
|
||||||
// given hash.
|
// given hash.
|
||||||
func (pool *LegacyPool) Has(hash common.Hash) bool {
|
func (pool *LegacyPool) Has(hash common.Hash) bool {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"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/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -133,11 +132,6 @@ type SubPool interface {
|
||||||
// given transaction hash.
|
// given transaction hash.
|
||||||
GetMetadata(hash common.Hash) *TxMetadata
|
GetMetadata(hash common.Hash) *TxMetadata
|
||||||
|
|
||||||
// GetBlobs returns a number of blobs are proofs for the given versioned hashes.
|
|
||||||
// This is a utility method for the engine API, enabling consensus clients to
|
|
||||||
// retrieve blobs from the pools directly instead of the network.
|
|
||||||
GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof)
|
|
||||||
|
|
||||||
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
// ValidateTxBasics checks whether a transaction is valid according to the consensus
|
||||||
// rules, but does not check state-dependent validation such as sufficient balance.
|
// rules, but does not check state-dependent validation such as sufficient balance.
|
||||||
// This check is meant as a static check which can be performed without holding the
|
// This check is meant as a static check which can be performed without holding the
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"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/crypto/kzg4844"
|
|
||||||
"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/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -308,22 +307,6 @@ func (p *TxPool) GetMetadata(hash common.Hash) *TxMetadata {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlobs returns a number of blobs are proofs for the given versioned hashes.
|
|
||||||
// This is a utility method for the engine API, enabling consensus clients to
|
|
||||||
// retrieve blobs from the pools directly instead of the network.
|
|
||||||
func (p *TxPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) {
|
|
||||||
for _, subpool := range p.subpools {
|
|
||||||
// It's an ugly to assume that only one pool will be capable of returning
|
|
||||||
// anything meaningful for this call, but anythingh else requires merging
|
|
||||||
// partial responses and that's too annoying to do until we get a second
|
|
||||||
// blobpool (probably never).
|
|
||||||
if blobs, proofs := subpool.GetBlobs(vhashes); blobs != nil {
|
|
||||||
return blobs, proofs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add enqueues a batch of transactions into the pool if they are valid. Due
|
// Add enqueues a batch of transactions into the pool if they are valid. Due
|
||||||
// to the large transaction churn, add may postpone fully integrating the tx
|
// to the large transaction churn, add may postpone fully integrating the tx
|
||||||
// to a later point to batch multiple ones together.
|
// to a later point to batch multiple ones together.
|
||||||
|
|
|
||||||
|
|
@ -138,14 +138,26 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.GasTipCap(), opts.MinTip)
|
return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.GasTipCap(), opts.MinTip)
|
||||||
}
|
}
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
// Ensure the blob fee cap satisfies the minimum blob gas price
|
return validateBlobTx(tx, head, opts)
|
||||||
if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 {
|
|
||||||
return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice)
|
|
||||||
}
|
}
|
||||||
|
if tx.Type() == types.SetCodeTxType {
|
||||||
|
if len(tx.SetCodeAuthorizations()) == 0 {
|
||||||
|
return fmt.Errorf("set code tx must have at least one authorization tuple")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateBlobTx implements the blob-transaction specific validations.
|
||||||
|
func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationOptions) error {
|
||||||
sidecar := tx.BlobTxSidecar()
|
sidecar := tx.BlobTxSidecar()
|
||||||
if sidecar == nil {
|
if sidecar == nil {
|
||||||
return errors.New("missing sidecar in blob transaction")
|
return errors.New("missing sidecar in blob transaction")
|
||||||
}
|
}
|
||||||
|
// Ensure the blob fee cap satisfies the minimum blob gas price
|
||||||
|
if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 {
|
||||||
|
return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice)
|
||||||
|
}
|
||||||
// Ensure the number of items in the blob transaction and various side
|
// Ensure the number of items in the blob transaction and various side
|
||||||
// data match up before doing any expensive validations
|
// data match up before doing any expensive validations
|
||||||
hashes := tx.BlobHashes()
|
hashes := tx.BlobHashes()
|
||||||
|
|
@ -156,31 +168,26 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
if len(hashes) > maxBlobs {
|
if len(hashes) > maxBlobs {
|
||||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs)
|
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs)
|
||||||
}
|
}
|
||||||
// Ensure commitments, proofs and hashes are valid
|
|
||||||
if err := validateBlobSidecar(hashes, sidecar); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tx.Type() == types.SetCodeTxType {
|
|
||||||
if len(tx.SetCodeAuthorizations()) == 0 {
|
|
||||||
return fmt.Errorf("set code tx must have at least one authorization tuple")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) error {
|
|
||||||
if len(sidecar.Blobs) != len(hashes) {
|
if len(sidecar.Blobs) != len(hashes) {
|
||||||
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
|
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
|
||||||
}
|
}
|
||||||
if len(sidecar.Proofs) != len(hashes) {
|
|
||||||
return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(sidecar.Proofs), len(hashes))
|
|
||||||
}
|
|
||||||
if err := sidecar.ValidateBlobCommitmentHashes(hashes); err != nil {
|
if err := sidecar.ValidateBlobCommitmentHashes(hashes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Blob commitments match with the hashes in the transaction, verify the
|
// Fork-specific sidecar checks, including proof verification.
|
||||||
// blobs themselves via KZG
|
if opts.Config.IsOsaka(head.Number, head.Time) {
|
||||||
|
return validateBlobSidecarOsaka(sidecar, hashes)
|
||||||
|
}
|
||||||
|
return validateBlobSidecarLegacy(sidecar, hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Hash) error {
|
||||||
|
if sidecar.Version != 0 {
|
||||||
|
return fmt.Errorf("invalid sidecar version pre-osaka: %v", sidecar.Version)
|
||||||
|
}
|
||||||
|
if len(sidecar.Proofs) != len(hashes) {
|
||||||
|
return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes))
|
||||||
|
}
|
||||||
for i := range sidecar.Blobs {
|
for i := range sidecar.Blobs {
|
||||||
if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
|
if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
|
||||||
return fmt.Errorf("invalid blob %d: %v", i, err)
|
return fmt.Errorf("invalid blob %d: %v", i, err)
|
||||||
|
|
@ -189,6 +196,16 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateBlobSidecarOsaka(sidecar *types.BlobTxSidecar, hashes []common.Hash) error {
|
||||||
|
if sidecar.Version != 1 {
|
||||||
|
return fmt.Errorf("invalid sidecar version post-osaka: %v", sidecar.Version)
|
||||||
|
}
|
||||||
|
if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob {
|
||||||
|
return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob)
|
||||||
|
}
|
||||||
|
return kzg4844.VerifyCellProofs(sidecar.Blobs, sidecar.Commitments, sidecar.Proofs)
|
||||||
|
}
|
||||||
|
|
||||||
// ValidationOptionsWithState define certain differences between stateful transaction
|
// ValidationOptionsWithState define certain differences between stateful transaction
|
||||||
// validation across the different pools without having to duplicate those checks.
|
// validation across the different pools without having to duplicate those checks.
|
||||||
type ValidationOptionsWithState struct {
|
type ValidationOptionsWithState struct {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ func (l Log) MarshalJSON() ([]byte, error) {
|
||||||
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||||
TxIndex hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
TxIndex hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
||||||
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
||||||
|
BlockTimestamp uint64 `json:"blockTimestamp" rlp:"-"`
|
||||||
Index hexutil.Uint `json:"logIndex" rlp:"-"`
|
Index hexutil.Uint `json:"logIndex" rlp:"-"`
|
||||||
Removed bool `json:"removed" rlp:"-"`
|
Removed bool `json:"removed" rlp:"-"`
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +34,7 @@ func (l Log) MarshalJSON() ([]byte, error) {
|
||||||
enc.TxHash = l.TxHash
|
enc.TxHash = l.TxHash
|
||||||
enc.TxIndex = hexutil.Uint(l.TxIndex)
|
enc.TxIndex = hexutil.Uint(l.TxIndex)
|
||||||
enc.BlockHash = l.BlockHash
|
enc.BlockHash = l.BlockHash
|
||||||
|
enc.BlockTimestamp = l.BlockTimestamp
|
||||||
enc.Index = hexutil.Uint(l.Index)
|
enc.Index = hexutil.Uint(l.Index)
|
||||||
enc.Removed = l.Removed
|
enc.Removed = l.Removed
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
|
|
@ -48,6 +50,7 @@ func (l *Log) UnmarshalJSON(input []byte) error {
|
||||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
TxHash *common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
|
||||||
TxIndex *hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
TxIndex *hexutil.Uint `json:"transactionIndex" rlp:"-"`
|
||||||
BlockHash *common.Hash `json:"blockHash" rlp:"-"`
|
BlockHash *common.Hash `json:"blockHash" rlp:"-"`
|
||||||
|
BlockTimestamp *uint64 `json:"blockTimestamp" rlp:"-"`
|
||||||
Index *hexutil.Uint `json:"logIndex" rlp:"-"`
|
Index *hexutil.Uint `json:"logIndex" rlp:"-"`
|
||||||
Removed *bool `json:"removed" rlp:"-"`
|
Removed *bool `json:"removed" rlp:"-"`
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +83,9 @@ func (l *Log) UnmarshalJSON(input []byte) error {
|
||||||
if dec.BlockHash != nil {
|
if dec.BlockHash != nil {
|
||||||
l.BlockHash = *dec.BlockHash
|
l.BlockHash = *dec.BlockHash
|
||||||
}
|
}
|
||||||
|
if dec.BlockTimestamp != nil {
|
||||||
|
l.BlockTimestamp = *dec.BlockTimestamp
|
||||||
|
}
|
||||||
if dec.Index != nil {
|
if dec.Index != nil {
|
||||||
l.Index = uint(*dec.Index)
|
l.Index = uint(*dec.Index)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ type Log struct {
|
||||||
TxIndex uint `json:"transactionIndex" rlp:"-"`
|
TxIndex uint `json:"transactionIndex" rlp:"-"`
|
||||||
// hash of the block in which the transaction was included
|
// hash of the block in which the transaction was included
|
||||||
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
BlockHash common.Hash `json:"blockHash" rlp:"-"`
|
||||||
|
// timestamp of the block in which the transaction was included
|
||||||
|
BlockTimestamp uint64 `json:"blockTimestamp" rlp:"-"`
|
||||||
// index of the log in the block
|
// index of the log in the block
|
||||||
Index uint `json:"logIndex" rlp:"-"`
|
Index uint `json:"logIndex" rlp:"-"`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -367,6 +367,7 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
|
||||||
for j := 0; j < len(rs[i].Logs); j++ {
|
for j := 0; j < len(rs[i].Logs); j++ {
|
||||||
rs[i].Logs[j].BlockNumber = number
|
rs[i].Logs[j].BlockNumber = number
|
||||||
rs[i].Logs[j].BlockHash = hash
|
rs[i].Logs[j].BlockHash = hash
|
||||||
|
rs[i].Logs[j].BlockTimestamp = time
|
||||||
rs[i].Logs[j].TxHash = rs[i].TxHash
|
rs[i].Logs[j].TxHash = rs[i].TxHash
|
||||||
rs[i].Logs[j].TxIndex = uint(i)
|
rs[i].Logs[j].TxIndex = uint(i)
|
||||||
rs[i].Logs[j].Index = logIndex
|
rs[i].Logs[j].Index = logIndex
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ func getTestReceipts() Receipts {
|
||||||
TxHash: txs[0].Hash(),
|
TxHash: txs[0].Hash(),
|
||||||
TxIndex: 0,
|
TxIndex: 0,
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
BlockTimestamp: blockTime,
|
||||||
Index: 0,
|
Index: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -187,6 +188,7 @@ func getTestReceipts() Receipts {
|
||||||
TxHash: txs[0].Hash(),
|
TxHash: txs[0].Hash(),
|
||||||
TxIndex: 0,
|
TxIndex: 0,
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
BlockTimestamp: blockTime,
|
||||||
Index: 1,
|
Index: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -211,6 +213,7 @@ func getTestReceipts() Receipts {
|
||||||
TxHash: txs[1].Hash(),
|
TxHash: txs[1].Hash(),
|
||||||
TxIndex: 1,
|
TxIndex: 1,
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
BlockTimestamp: blockTime,
|
||||||
Index: 2,
|
Index: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -221,6 +224,7 @@ func getTestReceipts() Receipts {
|
||||||
TxHash: txs[1].Hash(),
|
TxHash: txs[1].Hash(),
|
||||||
TxIndex: 1,
|
TxIndex: 1,
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
BlockTimestamp: blockTime,
|
||||||
Index: 3,
|
Index: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,12 @@ package types
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"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/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -55,6 +58,7 @@ type BlobTx struct {
|
||||||
|
|
||||||
// BlobTxSidecar contains the blobs of a blob transaction.
|
// BlobTxSidecar contains the blobs of a blob transaction.
|
||||||
type BlobTxSidecar struct {
|
type BlobTxSidecar struct {
|
||||||
|
Version byte // Version
|
||||||
Blobs []kzg4844.Blob // Blobs needed by the blob pool
|
Blobs []kzg4844.Blob // Blobs needed by the blob pool
|
||||||
Commitments []kzg4844.Commitment // Commitments needed by the blob pool
|
Commitments []kzg4844.Commitment // Commitments needed by the blob pool
|
||||||
Proofs []kzg4844.Proof // Proofs needed by the blob pool
|
Proofs []kzg4844.Proof // Proofs needed by the blob pool
|
||||||
|
|
@ -70,6 +74,20 @@ func (sc *BlobTxSidecar) BlobHashes() []common.Hash {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CellProofsAt returns the cell proofs for blob with index idx.
|
||||||
|
func (sc *BlobTxSidecar) CellProofsAt(idx int) []kzg4844.Proof {
|
||||||
|
var cellProofs []kzg4844.Proof
|
||||||
|
for i := range kzg4844.CellProofsPerBlob {
|
||||||
|
index := idx*kzg4844.CellProofsPerBlob + i
|
||||||
|
if index > len(sc.Proofs) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
proof := sc.Proofs[index]
|
||||||
|
cellProofs = append(cellProofs, proof)
|
||||||
|
}
|
||||||
|
return cellProofs
|
||||||
|
}
|
||||||
|
|
||||||
// encodedSize computes the RLP size of the sidecar elements. This does NOT return the
|
// encodedSize computes the RLP size of the sidecar elements. This does NOT return the
|
||||||
// encoded size of the BlobTxSidecar, it's just a helper for tx.Size().
|
// encoded size of the BlobTxSidecar, it's just a helper for tx.Size().
|
||||||
func (sc *BlobTxSidecar) encodedSize() uint64 {
|
func (sc *BlobTxSidecar) encodedSize() uint64 {
|
||||||
|
|
@ -102,14 +120,55 @@ func (sc *BlobTxSidecar) ValidateBlobCommitmentHashes(hashes []common.Hash) erro
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// blobTxWithBlobs is used for encoding of transactions when blobs are present.
|
// blobTxWithBlobs represents blob tx with its corresponding sidecar.
|
||||||
type blobTxWithBlobs struct {
|
// This is an interface because sidecars are versioned.
|
||||||
|
type blobTxWithBlobs interface {
|
||||||
|
tx() *BlobTx
|
||||||
|
assign(*BlobTxSidecar) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type blobTxWithBlobsV0 struct {
|
||||||
BlobTx *BlobTx
|
BlobTx *BlobTx
|
||||||
Blobs []kzg4844.Blob
|
Blobs []kzg4844.Blob
|
||||||
Commitments []kzg4844.Commitment
|
Commitments []kzg4844.Commitment
|
||||||
Proofs []kzg4844.Proof
|
Proofs []kzg4844.Proof
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type blobTxWithBlobsV1 struct {
|
||||||
|
BlobTx *BlobTx
|
||||||
|
Version byte
|
||||||
|
Blobs []kzg4844.Blob
|
||||||
|
Commitments []kzg4844.Commitment
|
||||||
|
Proofs []kzg4844.Proof
|
||||||
|
}
|
||||||
|
|
||||||
|
func (btx *blobTxWithBlobsV0) tx() *BlobTx {
|
||||||
|
return btx.BlobTx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (btx *blobTxWithBlobsV0) assign(sc *BlobTxSidecar) error {
|
||||||
|
sc.Version = 0
|
||||||
|
sc.Blobs = btx.Blobs
|
||||||
|
sc.Commitments = btx.Commitments
|
||||||
|
sc.Proofs = btx.Proofs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (btx *blobTxWithBlobsV1) tx() *BlobTx {
|
||||||
|
return btx.BlobTx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (btx *blobTxWithBlobsV1) assign(sc *BlobTxSidecar) error {
|
||||||
|
if btx.Version != 1 {
|
||||||
|
return fmt.Errorf("unsupported blob tx version %d", btx.Version)
|
||||||
|
}
|
||||||
|
sc.Version = 1
|
||||||
|
sc.Blobs = btx.Blobs
|
||||||
|
sc.Commitments = btx.Commitments
|
||||||
|
sc.Proofs = btx.Proofs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||||
func (tx *BlobTx) copy() TxData {
|
func (tx *BlobTx) copy() TxData {
|
||||||
cpy := &BlobTx{
|
cpy := &BlobTx{
|
||||||
|
|
@ -158,9 +217,9 @@ func (tx *BlobTx) copy() TxData {
|
||||||
}
|
}
|
||||||
if tx.Sidecar != nil {
|
if tx.Sidecar != nil {
|
||||||
cpy.Sidecar = &BlobTxSidecar{
|
cpy.Sidecar = &BlobTxSidecar{
|
||||||
Blobs: append([]kzg4844.Blob(nil), tx.Sidecar.Blobs...),
|
Blobs: slices.Clone(tx.Sidecar.Blobs),
|
||||||
Commitments: append([]kzg4844.Commitment(nil), tx.Sidecar.Commitments...),
|
Commitments: slices.Clone(tx.Sidecar.Commitments),
|
||||||
Proofs: append([]kzg4844.Proof(nil), tx.Sidecar.Proofs...),
|
Proofs: slices.Clone(tx.Sidecar.Proofs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cpy
|
return cpy
|
||||||
|
|
@ -215,48 +274,98 @@ func (tx *BlobTx) withSidecar(sideCar *BlobTxSidecar) *BlobTx {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *BlobTx) encode(b *bytes.Buffer) error {
|
func (tx *BlobTx) encode(b *bytes.Buffer) error {
|
||||||
if tx.Sidecar == nil {
|
switch {
|
||||||
|
case tx.Sidecar == nil:
|
||||||
return rlp.Encode(b, tx)
|
return rlp.Encode(b, tx)
|
||||||
}
|
|
||||||
inner := &blobTxWithBlobs{
|
case tx.Sidecar.Version == 0:
|
||||||
|
return rlp.Encode(b, &blobTxWithBlobsV0{
|
||||||
BlobTx: tx,
|
BlobTx: tx,
|
||||||
Blobs: tx.Sidecar.Blobs,
|
Blobs: tx.Sidecar.Blobs,
|
||||||
Commitments: tx.Sidecar.Commitments,
|
Commitments: tx.Sidecar.Commitments,
|
||||||
Proofs: tx.Sidecar.Proofs,
|
Proofs: tx.Sidecar.Proofs,
|
||||||
|
})
|
||||||
|
|
||||||
|
case tx.Sidecar.Version == 1:
|
||||||
|
return rlp.Encode(b, &blobTxWithBlobsV1{
|
||||||
|
BlobTx: tx,
|
||||||
|
Version: tx.Sidecar.Version,
|
||||||
|
Blobs: tx.Sidecar.Blobs,
|
||||||
|
Commitments: tx.Sidecar.Commitments,
|
||||||
|
Proofs: tx.Sidecar.Proofs,
|
||||||
|
})
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported sidecar version")
|
||||||
}
|
}
|
||||||
return rlp.Encode(b, inner)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *BlobTx) decode(input []byte) error {
|
func (tx *BlobTx) decode(input []byte) error {
|
||||||
// Here we need to support two formats: the network protocol encoding of the tx (with
|
// Here we need to support two outer formats: the network protocol encoding of the tx
|
||||||
// blobs) or the canonical encoding without blobs.
|
// (with blobs) or the canonical encoding without blobs.
|
||||||
//
|
//
|
||||||
// The two encodings can be distinguished by checking whether the first element of the
|
// The canonical encoding is just a list of fields:
|
||||||
// input list is itself a list.
|
//
|
||||||
|
// [chainID, nonce, ...]
|
||||||
|
//
|
||||||
|
// The network encoding is a list where the first element is the tx in the canonical encoding,
|
||||||
|
// and the remaining elements are the 'sidecar':
|
||||||
|
//
|
||||||
|
// [[chainID, nonce, ...], ...]
|
||||||
|
//
|
||||||
|
// The two outer encodings can be distinguished by checking whether the first element
|
||||||
|
// of the input list is itself a list. If it's the canonical encoding, the first
|
||||||
|
// element is the chainID, which is a number.
|
||||||
|
|
||||||
outerList, _, err := rlp.SplitList(input)
|
firstElem, _, err := rlp.SplitList(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
firstElemKind, _, _, err := rlp.Split(outerList)
|
firstElemKind, _, secondElem, err := rlp.Split(firstElem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if firstElemKind != rlp.List {
|
if firstElemKind != rlp.List {
|
||||||
|
// Blob tx without blobs.
|
||||||
return rlp.DecodeBytes(input, tx)
|
return rlp.DecodeBytes(input, tx)
|
||||||
}
|
}
|
||||||
// It's a tx with blobs.
|
|
||||||
var inner blobTxWithBlobs
|
// Now we know it's the network encoding with the blob sidecar. Here we again need to
|
||||||
if err := rlp.DecodeBytes(input, &inner); err != nil {
|
// support multiple encodings: legacy sidecars (v0) with a blob proof, and versioned
|
||||||
|
// sidecars.
|
||||||
|
//
|
||||||
|
// The legacy encoding is:
|
||||||
|
//
|
||||||
|
// [tx, blobs, commitments, proofs]
|
||||||
|
//
|
||||||
|
// The versioned encoding is:
|
||||||
|
//
|
||||||
|
// [tx, version, blobs, ...]
|
||||||
|
//
|
||||||
|
// We can tell the two apart by checking whether the second element is the version byte.
|
||||||
|
// For legacy sidecar the second element is a list of blobs.
|
||||||
|
|
||||||
|
secondElemKind, _, _, err := rlp.Split(secondElem)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*tx = *inner.BlobTx
|
var payload blobTxWithBlobs
|
||||||
tx.Sidecar = &BlobTxSidecar{
|
if secondElemKind == rlp.List {
|
||||||
Blobs: inner.Blobs,
|
// No version byte: blob sidecar v0.
|
||||||
Commitments: inner.Commitments,
|
payload = new(blobTxWithBlobsV0)
|
||||||
Proofs: inner.Proofs,
|
} else {
|
||||||
|
// It has a version byte. Decode as v1, version is checked by assign()
|
||||||
|
payload = new(blobTxWithBlobsV1)
|
||||||
}
|
}
|
||||||
|
if err := rlp.DecodeBytes(input, payload); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := new(BlobTxSidecar)
|
||||||
|
if err := payload.assign(sc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*tx = *payload.tx()
|
||||||
|
tx.Sidecar = sc
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,98 +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 vm
|
|
||||||
|
|
||||||
// eofCodeBitmap collects data locations in code.
|
|
||||||
func eofCodeBitmap(code []byte) bitvec {
|
|
||||||
// The bitmap is 4 bytes longer than necessary, in case the code
|
|
||||||
// ends with a PUSH32, the algorithm will push zeroes onto the
|
|
||||||
// bitvector outside the bounds of the actual code.
|
|
||||||
bits := make(bitvec, len(code)/8+1+4)
|
|
||||||
return eofCodeBitmapInternal(code, bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
// eofCodeBitmapInternal is the internal implementation of codeBitmap for EOF
|
|
||||||
// code validation.
|
|
||||||
func eofCodeBitmapInternal(code, bits bitvec) bitvec {
|
|
||||||
for pc := uint64(0); pc < uint64(len(code)); {
|
|
||||||
var (
|
|
||||||
op = OpCode(code[pc])
|
|
||||||
numbits uint16
|
|
||||||
)
|
|
||||||
pc++
|
|
||||||
|
|
||||||
if op == RJUMPV {
|
|
||||||
// RJUMPV is unique as it has a variable sized operand.
|
|
||||||
// The total size is determined by the count byte which
|
|
||||||
// immediate follows RJUMPV. Truncation will be caught
|
|
||||||
// in other validation steps -- for now, just return a
|
|
||||||
// valid bitmap for as much of the code as is
|
|
||||||
// available.
|
|
||||||
end := uint64(len(code))
|
|
||||||
if pc >= end {
|
|
||||||
// Count missing, no more bits to mark.
|
|
||||||
return bits
|
|
||||||
}
|
|
||||||
numbits = uint16(code[pc])*2 + 3
|
|
||||||
if pc+uint64(numbits) > end {
|
|
||||||
// Jump table is truncated, mark as many bits
|
|
||||||
// as possible.
|
|
||||||
numbits = uint16(end - pc)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
numbits = uint16(Immediates(op))
|
|
||||||
if numbits == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if numbits >= 8 {
|
|
||||||
for ; numbits >= 16; numbits -= 16 {
|
|
||||||
bits.set16(pc)
|
|
||||||
pc += 16
|
|
||||||
}
|
|
||||||
for ; numbits >= 8; numbits -= 8 {
|
|
||||||
bits.set8(pc)
|
|
||||||
pc += 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch numbits {
|
|
||||||
case 1:
|
|
||||||
bits.set1(pc)
|
|
||||||
pc += 1
|
|
||||||
case 2:
|
|
||||||
bits.setN(set2BitsMask, pc)
|
|
||||||
pc += 2
|
|
||||||
case 3:
|
|
||||||
bits.setN(set3BitsMask, pc)
|
|
||||||
pc += 3
|
|
||||||
case 4:
|
|
||||||
bits.setN(set4BitsMask, pc)
|
|
||||||
pc += 4
|
|
||||||
case 5:
|
|
||||||
bits.setN(set5BitsMask, pc)
|
|
||||||
pc += 5
|
|
||||||
case 6:
|
|
||||||
bits.setN(set6BitsMask, pc)
|
|
||||||
pc += 6
|
|
||||||
case 7:
|
|
||||||
bits.setN(set7BitsMask, pc)
|
|
||||||
pc += 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bits
|
|
||||||
}
|
|
||||||
|
|
@ -105,31 +105,3 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
||||||
op = STOP
|
op = STOP
|
||||||
bench.Run(op.String(), bencher)
|
bench.Run(op.String(), bencher)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkJumpdestOpEOFAnalysis(bench *testing.B) {
|
|
||||||
var op OpCode
|
|
||||||
bencher := func(b *testing.B) {
|
|
||||||
code := make([]byte, analysisCodeSize)
|
|
||||||
b.SetBytes(analysisCodeSize)
|
|
||||||
for i := range code {
|
|
||||||
code[i] = byte(op)
|
|
||||||
}
|
|
||||||
bits := make(bitvec, len(code)/8+1+4)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
clear(bits)
|
|
||||||
eofCodeBitmapInternal(code, bits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for op = PUSH1; op <= PUSH32; op++ {
|
|
||||||
bench.Run(op.String(), bencher)
|
|
||||||
}
|
|
||||||
op = JUMPDEST
|
|
||||||
bench.Run(op.String(), bencher)
|
|
||||||
op = STOP
|
|
||||||
bench.Run(op.String(), bencher)
|
|
||||||
op = RJUMPV
|
|
||||||
bench.Run(op.String(), bencher)
|
|
||||||
op = EOFCREATE
|
|
||||||
bench.Run(op.String(), bencher)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ var PrecompiledContractsByzantium = PrecompiledContracts{
|
||||||
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
||||||
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
||||||
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false},
|
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false, eip7823: false, eip7883: false},
|
||||||
common.BytesToAddress([]byte{0x6}): &bn256AddByzantium{},
|
common.BytesToAddress([]byte{0x6}): &bn256AddByzantium{},
|
||||||
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulByzantium{},
|
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulByzantium{},
|
||||||
common.BytesToAddress([]byte{0x8}): &bn256PairingByzantium{},
|
common.BytesToAddress([]byte{0x8}): &bn256PairingByzantium{},
|
||||||
|
|
@ -79,7 +79,7 @@ var PrecompiledContractsIstanbul = PrecompiledContracts{
|
||||||
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
||||||
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
||||||
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false},
|
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: false, eip7823: false, eip7883: false},
|
||||||
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
|
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
|
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
|
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
|
||||||
|
|
@ -93,7 +93,7 @@ var PrecompiledContractsBerlin = PrecompiledContracts{
|
||||||
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
||||||
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
||||||
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
|
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true, eip7823: false, eip7883: false},
|
||||||
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
|
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
|
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
|
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
|
||||||
|
|
@ -107,7 +107,7 @@ var PrecompiledContractsCancun = PrecompiledContracts{
|
||||||
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
common.BytesToAddress([]byte{0x2}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
|
||||||
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
common.BytesToAddress([]byte{0x4}): &dataCopy{},
|
||||||
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
|
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true, eip7823: false, eip7883: false},
|
||||||
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
|
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
|
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
|
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
|
||||||
|
|
@ -122,7 +122,7 @@ var PrecompiledContractsPrague = PrecompiledContracts{
|
||||||
common.BytesToAddress([]byte{0x02}): &sha256hash{},
|
common.BytesToAddress([]byte{0x02}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{0x03}): &ripemd160hash{},
|
common.BytesToAddress([]byte{0x03}): &ripemd160hash{},
|
||||||
common.BytesToAddress([]byte{0x04}): &dataCopy{},
|
common.BytesToAddress([]byte{0x04}): &dataCopy{},
|
||||||
common.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true},
|
common.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true, eip7823: false, eip7883: false},
|
||||||
common.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{},
|
common.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{},
|
common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{},
|
||||||
common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{},
|
common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{},
|
||||||
|
|
@ -141,7 +141,30 @@ var PrecompiledContractsBLS = PrecompiledContractsPrague
|
||||||
|
|
||||||
var PrecompiledContractsVerkle = PrecompiledContractsBerlin
|
var PrecompiledContractsVerkle = PrecompiledContractsBerlin
|
||||||
|
|
||||||
|
// PrecompiledContractsOsaka contains the set of pre-compiled Ethereum
|
||||||
|
// contracts used in the Osaka release.
|
||||||
|
var PrecompiledContractsOsaka = PrecompiledContracts{
|
||||||
|
common.BytesToAddress([]byte{0x01}): &ecrecover{},
|
||||||
|
common.BytesToAddress([]byte{0x02}): &sha256hash{},
|
||||||
|
common.BytesToAddress([]byte{0x03}): &ripemd160hash{},
|
||||||
|
common.BytesToAddress([]byte{0x04}): &dataCopy{},
|
||||||
|
common.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true, eip7823: true, eip7883: true},
|
||||||
|
common.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{},
|
||||||
|
common.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{},
|
||||||
|
common.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{},
|
||||||
|
common.BytesToAddress([]byte{0x09}): &blake2F{},
|
||||||
|
common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{},
|
||||||
|
common.BytesToAddress([]byte{0x0b}): &bls12381G1Add{},
|
||||||
|
common.BytesToAddress([]byte{0x0c}): &bls12381G1MultiExp{},
|
||||||
|
common.BytesToAddress([]byte{0x0d}): &bls12381G2Add{},
|
||||||
|
common.BytesToAddress([]byte{0x0e}): &bls12381G2MultiExp{},
|
||||||
|
common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{},
|
||||||
|
common.BytesToAddress([]byte{0x10}): &bls12381MapG1{},
|
||||||
|
common.BytesToAddress([]byte{0x11}): &bls12381MapG2{},
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
PrecompiledAddressesOsaka []common.Address
|
||||||
PrecompiledAddressesPrague []common.Address
|
PrecompiledAddressesPrague []common.Address
|
||||||
PrecompiledAddressesCancun []common.Address
|
PrecompiledAddressesCancun []common.Address
|
||||||
PrecompiledAddressesBerlin []common.Address
|
PrecompiledAddressesBerlin []common.Address
|
||||||
|
|
@ -169,12 +192,17 @@ func init() {
|
||||||
for k := range PrecompiledContractsPrague {
|
for k := range PrecompiledContractsPrague {
|
||||||
PrecompiledAddressesPrague = append(PrecompiledAddressesPrague, k)
|
PrecompiledAddressesPrague = append(PrecompiledAddressesPrague, k)
|
||||||
}
|
}
|
||||||
|
for k := range PrecompiledContractsOsaka {
|
||||||
|
PrecompiledAddressesOsaka = append(PrecompiledAddressesOsaka, k)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func activePrecompiledContracts(rules params.Rules) PrecompiledContracts {
|
func activePrecompiledContracts(rules params.Rules) PrecompiledContracts {
|
||||||
switch {
|
switch {
|
||||||
case rules.IsVerkle:
|
case rules.IsVerkle:
|
||||||
return PrecompiledContractsVerkle
|
return PrecompiledContractsVerkle
|
||||||
|
case rules.IsOsaka:
|
||||||
|
return PrecompiledContractsOsaka
|
||||||
case rules.IsPrague:
|
case rules.IsPrague:
|
||||||
return PrecompiledContractsPrague
|
return PrecompiledContractsPrague
|
||||||
case rules.IsCancun:
|
case rules.IsCancun:
|
||||||
|
|
@ -198,6 +226,8 @@ func ActivePrecompiledContracts(rules params.Rules) PrecompiledContracts {
|
||||||
// ActivePrecompiles returns the precompile addresses enabled with the current configuration.
|
// ActivePrecompiles returns the precompile addresses enabled with the current configuration.
|
||||||
func ActivePrecompiles(rules params.Rules) []common.Address {
|
func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||||
switch {
|
switch {
|
||||||
|
case rules.IsOsaka:
|
||||||
|
return PrecompiledAddressesOsaka
|
||||||
case rules.IsPrague:
|
case rules.IsPrague:
|
||||||
return PrecompiledAddressesPrague
|
return PrecompiledAddressesPrague
|
||||||
case rules.IsCancun:
|
case rules.IsCancun:
|
||||||
|
|
@ -317,6 +347,8 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
|
||||||
// bigModExp implements a native big integer exponential modular operation.
|
// bigModExp implements a native big integer exponential modular operation.
|
||||||
type bigModExp struct {
|
type bigModExp struct {
|
||||||
eip2565 bool
|
eip2565 bool
|
||||||
|
eip7823 bool
|
||||||
|
eip7883 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -392,8 +424,12 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||||
adjExpLen := new(big.Int)
|
adjExpLen := new(big.Int)
|
||||||
if expLen.Cmp(big32) > 0 {
|
if expLen.Cmp(big32) > 0 {
|
||||||
adjExpLen.Sub(expLen, big32)
|
adjExpLen.Sub(expLen, big32)
|
||||||
|
if c.eip7883 {
|
||||||
|
adjExpLen.Lsh(adjExpLen, 4)
|
||||||
|
} else {
|
||||||
adjExpLen.Lsh(adjExpLen, 3)
|
adjExpLen.Lsh(adjExpLen, 3)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
|
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
|
||||||
// Calculate the gas cost of the operation
|
// Calculate the gas cost of the operation
|
||||||
gas := new(big.Int)
|
gas := new(big.Int)
|
||||||
|
|
@ -402,19 +438,32 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||||
} else {
|
} else {
|
||||||
gas.Set(modLen)
|
gas.Set(modLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxLenOver32 := gas.Cmp(big32) > 0
|
||||||
if c.eip2565 {
|
if c.eip2565 {
|
||||||
// EIP-2565 has three changes
|
// EIP-2565 (Berlin fork) has three changes:
|
||||||
|
//
|
||||||
// 1. Different multComplexity (inlined here)
|
// 1. Different multComplexity (inlined here)
|
||||||
// in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
|
// in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
|
||||||
//
|
//
|
||||||
// def mult_complexity(x):
|
// def mult_complexity(x):
|
||||||
// ceiling(x/8)^2
|
// ceiling(x/8)^2
|
||||||
//
|
//
|
||||||
//where is x is max(length_of_MODULUS, length_of_BASE)
|
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||||
gas.Add(gas, big7)
|
gas.Add(gas, big7)
|
||||||
gas.Rsh(gas, 3)
|
gas.Rsh(gas, 3)
|
||||||
gas.Mul(gas, gas)
|
gas.Mul(gas, gas)
|
||||||
|
|
||||||
|
var minPrice uint64 = 200
|
||||||
|
if c.eip7883 {
|
||||||
|
minPrice = 500
|
||||||
|
if maxLenOver32 {
|
||||||
|
gas.Add(gas, gas)
|
||||||
|
} else {
|
||||||
|
gas = big.NewInt(16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if adjExpLen.Cmp(big1) > 0 {
|
if adjExpLen.Cmp(big1) > 0 {
|
||||||
gas.Mul(gas, adjExpLen)
|
gas.Mul(gas, adjExpLen)
|
||||||
}
|
}
|
||||||
|
|
@ -423,18 +472,15 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||||
if gas.BitLen() > 64 {
|
if gas.BitLen() > 64 {
|
||||||
return math.MaxUint64
|
return math.MaxUint64
|
||||||
}
|
}
|
||||||
// 3. Minimum price of 200 gas
|
return max(minPrice, gas.Uint64())
|
||||||
if gas.Uint64() < 200 {
|
|
||||||
return 200
|
|
||||||
}
|
|
||||||
return gas.Uint64()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-Berlin logic.
|
||||||
gas = modexpMultComplexity(gas)
|
gas = modexpMultComplexity(gas)
|
||||||
if adjExpLen.Cmp(big1) > 0 {
|
if adjExpLen.Cmp(big1) > 0 {
|
||||||
gas.Mul(gas, adjExpLen)
|
gas.Mul(gas, adjExpLen)
|
||||||
}
|
}
|
||||||
gas.Div(gas, big20)
|
gas.Div(gas, big20)
|
||||||
|
|
||||||
if gas.BitLen() > 64 {
|
if gas.BitLen() > 64 {
|
||||||
return math.MaxUint64
|
return math.MaxUint64
|
||||||
}
|
}
|
||||||
|
|
@ -456,6 +502,10 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
||||||
if baseLen == 0 && modLen == 0 {
|
if baseLen == 0 && modLen == 0 {
|
||||||
return []byte{}, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
// enforce size cap for inputs
|
||||||
|
if c.eip7823 && max(baseLen, expLen, modLen) > 1024 {
|
||||||
|
return nil, fmt.Errorf("one or more of base/exponent/modulus length exceeded 1024 bytes")
|
||||||
|
}
|
||||||
// Retrieve the operands and execute the exponentiation
|
// Retrieve the operands and execute the exponentiation
|
||||||
var (
|
var (
|
||||||
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
|
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,9 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
|
||||||
common.BytesToAddress([]byte{2}): &sha256hash{},
|
common.BytesToAddress([]byte{2}): &sha256hash{},
|
||||||
common.BytesToAddress([]byte{3}): &ripemd160hash{},
|
common.BytesToAddress([]byte{3}): &ripemd160hash{},
|
||||||
common.BytesToAddress([]byte{4}): &dataCopy{},
|
common.BytesToAddress([]byte{4}): &dataCopy{},
|
||||||
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
|
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false, eip7883: false},
|
||||||
common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true},
|
common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true, eip7883: false},
|
||||||
|
common.BytesToAddress([]byte{0xf6}): &bigModExp{eip2565: true, eip7883: true},
|
||||||
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
|
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
|
||||||
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
|
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
|
||||||
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
|
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
|
||||||
|
|
@ -238,6 +239,9 @@ func BenchmarkPrecompiledModExp(b *testing.B) { benchJson("modexp", "05", b) }
|
||||||
func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f5", t) }
|
func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f5", t) }
|
||||||
func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f5", b) }
|
func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f5", b) }
|
||||||
|
|
||||||
|
func TestPrecompiledModExpEip7883(t *testing.T) { testJson("modexp_eip7883", "f6", t) }
|
||||||
|
func BenchmarkPrecompiledModExpEip7883(b *testing.B) { benchJson("modexp_eip7883", "f6", b) }
|
||||||
|
|
||||||
// Tests the sample inputs from the elliptic curve addition EIP 213.
|
// Tests the sample inputs from the elliptic curve addition EIP 213.
|
||||||
func TestPrecompiledBn256Add(t *testing.T) { testJson("bn256Add", "06", t) }
|
func TestPrecompiledBn256Add(t *testing.T) { testJson("bn256Add", "06", t) }
|
||||||
func BenchmarkPrecompiledBn256Add(b *testing.B) { benchJson("bn256Add", "06", b) }
|
func BenchmarkPrecompiledBn256Add(b *testing.B) { benchJson("bn256Add", "06", b) }
|
||||||
|
|
|
||||||
170
core/vm/eips.go
170
core/vm/eips.go
|
|
@ -531,176 +531,6 @@ func enable4762(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enableEOF applies the EOF changes.
|
|
||||||
// OBS! For EOF, there are two changes:
|
|
||||||
// 1. Two separate jumptables are required. One, EOF-jumptable, is used by
|
|
||||||
// eof contracts. This one contains things like RJUMP.
|
|
||||||
// 2. The regular non-eof jumptable also needs to be modified, specifically to
|
|
||||||
// modify how EXTCODECOPY works under the hood.
|
|
||||||
//
|
|
||||||
// This method _only_ deals with case 1.
|
|
||||||
func enableEOF(jt *JumpTable) {
|
|
||||||
// Deprecate opcodes
|
|
||||||
undefined := &operation{
|
|
||||||
execute: opUndefined,
|
|
||||||
constantGas: 0,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
undefined: true,
|
|
||||||
}
|
|
||||||
jt[CALL] = undefined
|
|
||||||
jt[CALLCODE] = undefined
|
|
||||||
jt[DELEGATECALL] = undefined
|
|
||||||
jt[STATICCALL] = undefined
|
|
||||||
jt[SELFDESTRUCT] = undefined
|
|
||||||
jt[JUMP] = undefined
|
|
||||||
jt[JUMPI] = undefined
|
|
||||||
jt[PC] = undefined
|
|
||||||
jt[CREATE] = undefined
|
|
||||||
jt[CREATE2] = undefined
|
|
||||||
jt[CODESIZE] = undefined
|
|
||||||
jt[CODECOPY] = undefined
|
|
||||||
jt[EXTCODESIZE] = undefined
|
|
||||||
jt[EXTCODECOPY] = undefined
|
|
||||||
jt[EXTCODEHASH] = undefined
|
|
||||||
jt[GAS] = undefined
|
|
||||||
// Allow 0xFE to terminate sections
|
|
||||||
jt[INVALID] = &operation{
|
|
||||||
execute: opUndefined,
|
|
||||||
constantGas: 0,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
// New opcodes
|
|
||||||
jt[RJUMP] = &operation{
|
|
||||||
execute: opRjump,
|
|
||||||
constantGas: GasQuickStep,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
jt[RJUMPI] = &operation{
|
|
||||||
execute: opRjumpi,
|
|
||||||
constantGas: GasFastishStep,
|
|
||||||
minStack: minStack(1, 0),
|
|
||||||
maxStack: maxStack(1, 0),
|
|
||||||
}
|
|
||||||
jt[RJUMPV] = &operation{
|
|
||||||
execute: opRjumpv,
|
|
||||||
constantGas: GasFastishStep,
|
|
||||||
minStack: minStack(1, 0),
|
|
||||||
maxStack: maxStack(1, 0),
|
|
||||||
}
|
|
||||||
jt[CALLF] = &operation{
|
|
||||||
execute: opCallf,
|
|
||||||
constantGas: GasFastStep,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
jt[RETF] = &operation{
|
|
||||||
execute: opRetf,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
jt[JUMPF] = &operation{
|
|
||||||
execute: opJumpf,
|
|
||||||
constantGas: GasFastStep,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
jt[EOFCREATE] = &operation{
|
|
||||||
execute: opEOFCreate,
|
|
||||||
constantGas: params.Create2Gas,
|
|
||||||
dynamicGas: gasEOFCreate,
|
|
||||||
minStack: minStack(4, 1),
|
|
||||||
maxStack: maxStack(4, 1),
|
|
||||||
memorySize: memoryEOFCreate,
|
|
||||||
}
|
|
||||||
jt[RETURNCONTRACT] = &operation{
|
|
||||||
execute: opReturnContract,
|
|
||||||
// returncontract has zero constant gas cost
|
|
||||||
dynamicGas: pureMemoryGascost,
|
|
||||||
minStack: minStack(2, 0),
|
|
||||||
maxStack: maxStack(2, 0),
|
|
||||||
memorySize: memoryReturnContract,
|
|
||||||
}
|
|
||||||
jt[DATALOAD] = &operation{
|
|
||||||
execute: opDataLoad,
|
|
||||||
constantGas: GasFastishStep,
|
|
||||||
minStack: minStack(1, 1),
|
|
||||||
maxStack: maxStack(1, 1),
|
|
||||||
}
|
|
||||||
jt[DATALOADN] = &operation{
|
|
||||||
execute: opDataLoadN,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
minStack: minStack(0, 1),
|
|
||||||
maxStack: maxStack(0, 1),
|
|
||||||
}
|
|
||||||
jt[DATASIZE] = &operation{
|
|
||||||
execute: opDataSize,
|
|
||||||
constantGas: GasQuickStep,
|
|
||||||
minStack: minStack(0, 1),
|
|
||||||
maxStack: maxStack(0, 1),
|
|
||||||
}
|
|
||||||
jt[DATACOPY] = &operation{
|
|
||||||
execute: opDataCopy,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
dynamicGas: memoryCopierGas(2),
|
|
||||||
minStack: minStack(3, 0),
|
|
||||||
maxStack: maxStack(3, 0),
|
|
||||||
memorySize: memoryDataCopy,
|
|
||||||
}
|
|
||||||
jt[DUPN] = &operation{
|
|
||||||
execute: opDupN,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
minStack: minStack(0, 1),
|
|
||||||
maxStack: maxStack(0, 1),
|
|
||||||
}
|
|
||||||
jt[SWAPN] = &operation{
|
|
||||||
execute: opSwapN,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
jt[EXCHANGE] = &operation{
|
|
||||||
execute: opExchange,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
minStack: minStack(0, 0),
|
|
||||||
maxStack: maxStack(0, 0),
|
|
||||||
}
|
|
||||||
jt[RETURNDATALOAD] = &operation{
|
|
||||||
execute: opReturnDataLoad,
|
|
||||||
constantGas: GasFastestStep,
|
|
||||||
minStack: minStack(1, 1),
|
|
||||||
maxStack: maxStack(1, 1),
|
|
||||||
}
|
|
||||||
jt[EXTCALL] = &operation{
|
|
||||||
execute: opExtCall,
|
|
||||||
constantGas: params.WarmStorageReadCostEIP2929,
|
|
||||||
dynamicGas: makeCallVariantGasCallEIP2929(gasExtCall, 0),
|
|
||||||
minStack: minStack(4, 1),
|
|
||||||
maxStack: maxStack(4, 1),
|
|
||||||
memorySize: memoryExtCall,
|
|
||||||
}
|
|
||||||
jt[EXTDELEGATECALL] = &operation{
|
|
||||||
execute: opExtDelegateCall,
|
|
||||||
dynamicGas: makeCallVariantGasCallEIP2929(gasExtDelegateCall, 0),
|
|
||||||
constantGas: params.WarmStorageReadCostEIP2929,
|
|
||||||
minStack: minStack(3, 1),
|
|
||||||
maxStack: maxStack(3, 1),
|
|
||||||
memorySize: memoryExtCall,
|
|
||||||
}
|
|
||||||
jt[EXTSTATICCALL] = &operation{
|
|
||||||
execute: opExtStaticCall,
|
|
||||||
constantGas: params.WarmStorageReadCostEIP2929,
|
|
||||||
dynamicGas: makeCallVariantGasCallEIP2929(gasExtStaticCall, 0),
|
|
||||||
minStack: minStack(3, 1),
|
|
||||||
maxStack: maxStack(3, 1),
|
|
||||||
memorySize: memoryExtCall,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable7702 the EIP-7702 changes to support delegation designators.
|
// enable7702 the EIP-7702 changes to support delegation designators.
|
||||||
func enable7702(jt *JumpTable) {
|
func enable7702(jt *JumpTable) {
|
||||||
jt[CALL].dynamicGas = gasCallEIP7702
|
jt[CALL].dynamicGas = gasCallEIP7702
|
||||||
|
|
|
||||||
501
core/vm/eof.go
501
core/vm/eof.go
|
|
@ -1,501 +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 vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
offsetVersion = 2
|
|
||||||
offsetTypesKind = 3
|
|
||||||
offsetCodeKind = 6
|
|
||||||
|
|
||||||
kindTypes = 1
|
|
||||||
kindCode = 2
|
|
||||||
kindContainer = 3
|
|
||||||
kindData = 4
|
|
||||||
|
|
||||||
eofFormatByte = 0xef
|
|
||||||
eof1Version = 1
|
|
||||||
|
|
||||||
maxInputItems = 127
|
|
||||||
maxOutputItems = 128
|
|
||||||
maxStackHeight = 1023
|
|
||||||
maxContainerSections = 256
|
|
||||||
)
|
|
||||||
|
|
||||||
var eofMagic = []byte{0xef, 0x00}
|
|
||||||
|
|
||||||
// HasEOFByte returns true if code starts with 0xEF byte
|
|
||||||
func HasEOFByte(code []byte) bool {
|
|
||||||
return len(code) != 0 && code[0] == eofFormatByte
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasEOFMagic returns true if code starts with magic defined by EIP-3540
|
|
||||||
func hasEOFMagic(code []byte) bool {
|
|
||||||
return len(eofMagic) <= len(code) && bytes.Equal(eofMagic, code[0:len(eofMagic)])
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEOFVersion1 returns true if the code's version byte equals eof1Version. It
|
|
||||||
// does not verify the EOF magic is valid.
|
|
||||||
func isEOFVersion1(code []byte) bool {
|
|
||||||
return 2 < len(code) && code[2] == byte(eof1Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container is an EOF container object.
|
|
||||||
type Container struct {
|
|
||||||
types []*functionMetadata
|
|
||||||
codeSections [][]byte
|
|
||||||
subContainers []*Container
|
|
||||||
subContainerCodes [][]byte
|
|
||||||
data []byte
|
|
||||||
dataSize int // might be more than len(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// functionMetadata is an EOF function signature.
|
|
||||||
type functionMetadata struct {
|
|
||||||
inputs uint8
|
|
||||||
outputs uint8
|
|
||||||
maxStackHeight uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// stackDelta returns the #outputs - #inputs
|
|
||||||
func (meta *functionMetadata) stackDelta() int {
|
|
||||||
return int(meta.outputs) - int(meta.inputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkInputs checks the current minimum stack (stackMin) against the required inputs
|
|
||||||
// of the metadata, and returns an error if the stack is too shallow.
|
|
||||||
func (meta *functionMetadata) checkInputs(stackMin int) error {
|
|
||||||
if int(meta.inputs) > stackMin {
|
|
||||||
return ErrStackUnderflow{stackLen: stackMin, required: int(meta.inputs)}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkStackMax checks the if current maximum stack combined with the
|
|
||||||
// function max stack will result in a stack overflow, and if so returns an error.
|
|
||||||
func (meta *functionMetadata) checkStackMax(stackMax int) error {
|
|
||||||
newMaxStack := stackMax + int(meta.maxStackHeight) - int(meta.inputs)
|
|
||||||
if newMaxStack > int(params.StackLimit) {
|
|
||||||
return ErrStackOverflow{stackLen: newMaxStack, limit: int(params.StackLimit)}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary encodes an EOF container into binary format.
|
|
||||||
func (c *Container) MarshalBinary() []byte {
|
|
||||||
// Build EOF prefix.
|
|
||||||
b := make([]byte, 2)
|
|
||||||
copy(b, eofMagic)
|
|
||||||
b = append(b, eof1Version)
|
|
||||||
|
|
||||||
// Write section headers.
|
|
||||||
b = append(b, kindTypes)
|
|
||||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4))
|
|
||||||
b = append(b, kindCode)
|
|
||||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSections)))
|
|
||||||
for _, codeSection := range c.codeSections {
|
|
||||||
b = binary.BigEndian.AppendUint16(b, uint16(len(codeSection)))
|
|
||||||
}
|
|
||||||
var encodedContainer [][]byte
|
|
||||||
if len(c.subContainers) != 0 {
|
|
||||||
b = append(b, kindContainer)
|
|
||||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.subContainers)))
|
|
||||||
for _, section := range c.subContainers {
|
|
||||||
encoded := section.MarshalBinary()
|
|
||||||
b = binary.BigEndian.AppendUint16(b, uint16(len(encoded)))
|
|
||||||
encodedContainer = append(encodedContainer, encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b = append(b, kindData)
|
|
||||||
b = binary.BigEndian.AppendUint16(b, uint16(c.dataSize))
|
|
||||||
b = append(b, 0) // terminator
|
|
||||||
|
|
||||||
// Write section contents.
|
|
||||||
for _, ty := range c.types {
|
|
||||||
b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...)
|
|
||||||
}
|
|
||||||
for _, code := range c.codeSections {
|
|
||||||
b = append(b, code...)
|
|
||||||
}
|
|
||||||
for _, section := range encodedContainer {
|
|
||||||
b = append(b, section...)
|
|
||||||
}
|
|
||||||
b = append(b, c.data...)
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary decodes an EOF container.
|
|
||||||
func (c *Container) UnmarshalBinary(b []byte, isInitcode bool) error {
|
|
||||||
return c.unmarshalContainer(b, isInitcode, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalSubContainer decodes an EOF container that is inside another container.
|
|
||||||
func (c *Container) UnmarshalSubContainer(b []byte, isInitcode bool) error {
|
|
||||||
return c.unmarshalContainer(b, isInitcode, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) error {
|
|
||||||
if !hasEOFMagic(b) {
|
|
||||||
return fmt.Errorf("%w: want %x", errInvalidMagic, eofMagic)
|
|
||||||
}
|
|
||||||
if len(b) < 14 {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if len(b) > params.MaxInitCodeSize {
|
|
||||||
return ErrMaxInitCodeSizeExceeded
|
|
||||||
}
|
|
||||||
if !isEOFVersion1(b) {
|
|
||||||
return fmt.Errorf("%w: have %d, want %d", errInvalidVersion, b[2], eof1Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
kind, typesSize, dataSize int
|
|
||||||
codeSizes []int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse type section header.
|
|
||||||
kind, typesSize, err = parseSection(b, offsetTypesKind)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if kind != kindTypes {
|
|
||||||
return fmt.Errorf("%w: found section kind %x instead", errMissingTypeHeader, kind)
|
|
||||||
}
|
|
||||||
if typesSize < 4 || typesSize%4 != 0 {
|
|
||||||
return fmt.Errorf("%w: type section size must be divisible by 4, have %d", errInvalidTypeSize, typesSize)
|
|
||||||
}
|
|
||||||
if typesSize/4 > 1024 {
|
|
||||||
return fmt.Errorf("%w: type section must not exceed 4*1024, have %d", errInvalidTypeSize, typesSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse code section header.
|
|
||||||
kind, codeSizes, err = parseSectionList(b, offsetCodeKind)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if kind != kindCode {
|
|
||||||
return fmt.Errorf("%w: found section kind %x instead", errMissingCodeHeader, kind)
|
|
||||||
}
|
|
||||||
if len(codeSizes) != typesSize/4 {
|
|
||||||
return fmt.Errorf("%w: mismatch of code sections found and type signatures, types %d, code %d", errInvalidCodeSize, typesSize/4, len(codeSizes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse (optional) container section header.
|
|
||||||
var containerSizes []int
|
|
||||||
offset := offsetCodeKind + 2 + 2*len(codeSizes) + 1
|
|
||||||
if offset < len(b) && b[offset] == kindContainer {
|
|
||||||
kind, containerSizes, err = parseSectionList(b, offset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if kind != kindContainer {
|
|
||||||
panic("somethings wrong")
|
|
||||||
}
|
|
||||||
if len(containerSizes) == 0 {
|
|
||||||
return fmt.Errorf("%w: total container count must not be zero", errInvalidContainerSectionSize)
|
|
||||||
}
|
|
||||||
offset = offset + 2 + 2*len(containerSizes) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse data section header.
|
|
||||||
kind, dataSize, err = parseSection(b, offset)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if kind != kindData {
|
|
||||||
return fmt.Errorf("%w: found section %x instead", errMissingDataHeader, kind)
|
|
||||||
}
|
|
||||||
c.dataSize = dataSize
|
|
||||||
|
|
||||||
// Check for terminator.
|
|
||||||
offsetTerminator := offset + 3
|
|
||||||
if len(b) < offsetTerminator {
|
|
||||||
return fmt.Errorf("%w: invalid offset terminator", io.ErrUnexpectedEOF)
|
|
||||||
}
|
|
||||||
if b[offsetTerminator] != 0 {
|
|
||||||
return fmt.Errorf("%w: have %x", errMissingTerminator, b[offsetTerminator])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify overall container size.
|
|
||||||
expectedSize := offsetTerminator + typesSize + sum(codeSizes) + dataSize + 1
|
|
||||||
if len(containerSizes) != 0 {
|
|
||||||
expectedSize += sum(containerSizes)
|
|
||||||
}
|
|
||||||
if len(b) < expectedSize-dataSize {
|
|
||||||
return fmt.Errorf("%w: have %d, want %d", errInvalidContainerSize, len(b), expectedSize)
|
|
||||||
}
|
|
||||||
// Only check that the expected size is not exceed on non-initcode
|
|
||||||
if (!topLevel || !isInitcode) && len(b) > expectedSize {
|
|
||||||
return fmt.Errorf("%w: have %d, want %d", errInvalidContainerSize, len(b), expectedSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse types section.
|
|
||||||
idx := offsetTerminator + 1
|
|
||||||
var types = make([]*functionMetadata, 0, typesSize/4)
|
|
||||||
for i := 0; i < typesSize/4; i++ {
|
|
||||||
sig := &functionMetadata{
|
|
||||||
inputs: b[idx+i*4],
|
|
||||||
outputs: b[idx+i*4+1],
|
|
||||||
maxStackHeight: binary.BigEndian.Uint16(b[idx+i*4+2:]),
|
|
||||||
}
|
|
||||||
if sig.inputs > maxInputItems {
|
|
||||||
return fmt.Errorf("%w for section %d: have %d", errTooManyInputs, i, sig.inputs)
|
|
||||||
}
|
|
||||||
if sig.outputs > maxOutputItems {
|
|
||||||
return fmt.Errorf("%w for section %d: have %d", errTooManyOutputs, i, sig.outputs)
|
|
||||||
}
|
|
||||||
if sig.maxStackHeight > maxStackHeight {
|
|
||||||
return fmt.Errorf("%w for section %d: have %d", errTooLargeMaxStackHeight, i, sig.maxStackHeight)
|
|
||||||
}
|
|
||||||
types = append(types, sig)
|
|
||||||
}
|
|
||||||
if types[0].inputs != 0 || types[0].outputs != 0x80 {
|
|
||||||
return fmt.Errorf("%w: have %d, %d", errInvalidSection0Type, types[0].inputs, types[0].outputs)
|
|
||||||
}
|
|
||||||
c.types = types
|
|
||||||
|
|
||||||
// Parse code sections.
|
|
||||||
idx += typesSize
|
|
||||||
codeSections := make([][]byte, len(codeSizes))
|
|
||||||
for i, size := range codeSizes {
|
|
||||||
if size == 0 {
|
|
||||||
return fmt.Errorf("%w for section %d: size must not be 0", errInvalidCodeSize, i)
|
|
||||||
}
|
|
||||||
codeSections[i] = b[idx : idx+size]
|
|
||||||
idx += size
|
|
||||||
}
|
|
||||||
c.codeSections = codeSections
|
|
||||||
// Parse the optional container sizes.
|
|
||||||
if len(containerSizes) != 0 {
|
|
||||||
if len(containerSizes) > maxContainerSections {
|
|
||||||
return fmt.Errorf("%w number of container section exceed: %v: have %v", errInvalidContainerSectionSize, maxContainerSections, len(containerSizes))
|
|
||||||
}
|
|
||||||
subContainerCodes := make([][]byte, 0, len(containerSizes))
|
|
||||||
subContainers := make([]*Container, 0, len(containerSizes))
|
|
||||||
for i, size := range containerSizes {
|
|
||||||
if size == 0 || idx+size > len(b) {
|
|
||||||
return fmt.Errorf("%w for section %d: size must not be 0", errInvalidContainerSectionSize, i)
|
|
||||||
}
|
|
||||||
subC := new(Container)
|
|
||||||
end := min(idx+size, len(b))
|
|
||||||
if err := subC.unmarshalContainer(b[idx:end], isInitcode, false); err != nil {
|
|
||||||
if topLevel {
|
|
||||||
return fmt.Errorf("%w in sub container %d", err, i)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
subContainers = append(subContainers, subC)
|
|
||||||
subContainerCodes = append(subContainerCodes, b[idx:end])
|
|
||||||
|
|
||||||
idx += size
|
|
||||||
}
|
|
||||||
c.subContainers = subContainers
|
|
||||||
c.subContainerCodes = subContainerCodes
|
|
||||||
}
|
|
||||||
|
|
||||||
//Parse data section.
|
|
||||||
end := len(b)
|
|
||||||
if !isInitcode {
|
|
||||||
end = min(idx+dataSize, len(b))
|
|
||||||
}
|
|
||||||
if topLevel && len(b) != idx+dataSize {
|
|
||||||
return errTruncatedTopLevelContainer
|
|
||||||
}
|
|
||||||
c.data = b[idx:end]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateCode validates each code section of the container against the EOF v1
|
|
||||||
// rule set.
|
|
||||||
func (c *Container) ValidateCode(jt *JumpTable, isInitCode bool) error {
|
|
||||||
refBy := notRefByEither
|
|
||||||
if isInitCode {
|
|
||||||
refBy = refByEOFCreate
|
|
||||||
}
|
|
||||||
return c.validateSubContainer(jt, refBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error {
|
|
||||||
visited := make(map[int]struct{})
|
|
||||||
subContainerVisited := make(map[int]int)
|
|
||||||
toVisit := []int{0}
|
|
||||||
for len(toVisit) > 0 {
|
|
||||||
// TODO check if this can be used as a DOS
|
|
||||||
// Theres and edge case here where we mark something as visited that we visit before,
|
|
||||||
// This should not trigger a re-visit
|
|
||||||
// e.g. 0 -> 1, 2, 3
|
|
||||||
// 1 -> 2, 3
|
|
||||||
// should not mean 2 and 3 should be visited twice
|
|
||||||
var (
|
|
||||||
index = toVisit[0]
|
|
||||||
code = c.codeSections[index]
|
|
||||||
)
|
|
||||||
if _, ok := visited[index]; !ok {
|
|
||||||
res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
visited[index] = struct{}{}
|
|
||||||
// Mark all sections that can be visited from here.
|
|
||||||
for idx := range res.visitedCode {
|
|
||||||
if _, ok := visited[idx]; !ok {
|
|
||||||
toVisit = append(toVisit, idx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Mark all subcontainer that can be visited from here.
|
|
||||||
for idx, reference := range res.visitedSubContainers {
|
|
||||||
// Make sure subcontainers are only ever referenced by either EOFCreate or ReturnContract
|
|
||||||
if ref, ok := subContainerVisited[idx]; ok && ref != reference {
|
|
||||||
return errors.New("section referenced by both EOFCreate and ReturnContract")
|
|
||||||
}
|
|
||||||
subContainerVisited[idx] = reference
|
|
||||||
}
|
|
||||||
if refBy == refByReturnContract && res.isInitCode {
|
|
||||||
return errIncompatibleContainerKind
|
|
||||||
}
|
|
||||||
if refBy == refByEOFCreate && res.isRuntime {
|
|
||||||
return errIncompatibleContainerKind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toVisit = toVisit[1:]
|
|
||||||
}
|
|
||||||
// Make sure every code section is visited at least once.
|
|
||||||
if len(visited) != len(c.codeSections) {
|
|
||||||
return errUnreachableCode
|
|
||||||
}
|
|
||||||
for idx, container := range c.subContainers {
|
|
||||||
reference, ok := subContainerVisited[idx]
|
|
||||||
if !ok {
|
|
||||||
return errOrphanedSubcontainer
|
|
||||||
}
|
|
||||||
if err := container.validateSubContainer(jt, reference); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSection decodes a (kind, size) pair from an EOF header.
|
|
||||||
func parseSection(b []byte, idx int) (kind, size int, err error) {
|
|
||||||
if idx+3 >= len(b) {
|
|
||||||
return 0, 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
kind = int(b[idx])
|
|
||||||
size = int(binary.BigEndian.Uint16(b[idx+1:]))
|
|
||||||
return kind, size, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSectionList decodes a (kind, len, []codeSize) section list from an EOF
|
|
||||||
// header.
|
|
||||||
func parseSectionList(b []byte, idx int) (kind int, list []int, err error) {
|
|
||||||
if idx >= len(b) {
|
|
||||||
return 0, nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
kind = int(b[idx])
|
|
||||||
list, err = parseList(b, idx+1)
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
return kind, list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseList decodes a list of uint16..
|
|
||||||
func parseList(b []byte, idx int) ([]int, error) {
|
|
||||||
if len(b) < idx+2 {
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
count := binary.BigEndian.Uint16(b[idx:])
|
|
||||||
if len(b) <= idx+2+int(count)*2 {
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
list := make([]int, count)
|
|
||||||
for i := 0; i < int(count); i++ {
|
|
||||||
list[i] = int(binary.BigEndian.Uint16(b[idx+2+2*i:]))
|
|
||||||
}
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseUint16 parses a 16 bit unsigned integer.
|
|
||||||
func parseUint16(b []byte) (int, error) {
|
|
||||||
if len(b) < 2 {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return int(binary.BigEndian.Uint16(b)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseInt16 parses a 16 bit signed integer.
|
|
||||||
func parseInt16(b []byte) int {
|
|
||||||
return int(int16(b[1]) | int16(b[0])<<8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sum computes the sum of a slice.
|
|
||||||
func sum(list []int) (s int) {
|
|
||||||
for _, n := range list {
|
|
||||||
s += n
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) String() string {
|
|
||||||
var output = []string{
|
|
||||||
"Header",
|
|
||||||
fmt.Sprintf(" - EOFMagic: %02x", eofMagic),
|
|
||||||
fmt.Sprintf(" - EOFVersion: %02x", eof1Version),
|
|
||||||
fmt.Sprintf(" - KindType: %02x", kindTypes),
|
|
||||||
fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4),
|
|
||||||
fmt.Sprintf(" - KindCode: %02x", kindCode),
|
|
||||||
fmt.Sprintf(" - KindData: %02x", kindData),
|
|
||||||
fmt.Sprintf(" - DataSize: %04x", len(c.data)),
|
|
||||||
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSections)),
|
|
||||||
}
|
|
||||||
for i, code := range c.codeSections {
|
|
||||||
output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(code)))
|
|
||||||
}
|
|
||||||
|
|
||||||
output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.subContainers)))
|
|
||||||
if len(c.subContainers) > 0 {
|
|
||||||
for i, section := range c.subContainers {
|
|
||||||
output = append(output, fmt.Sprintf(" - subcontainer %d length: %04x\n", i, len(section.MarshalBinary())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output = append(output, "Body")
|
|
||||||
for i, typ := range c.types {
|
|
||||||
output = append(output, fmt.Sprintf(" - Type %v: %x", i,
|
|
||||||
[]byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)}))
|
|
||||||
}
|
|
||||||
for i, code := range c.codeSections {
|
|
||||||
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, code))
|
|
||||||
}
|
|
||||||
for i, section := range c.subContainers {
|
|
||||||
output = append(output, fmt.Sprintf(" - Subcontainer %d: %x", i, section.MarshalBinary()))
|
|
||||||
}
|
|
||||||
output = append(output, fmt.Sprintf(" - Data: %#x", c.data))
|
|
||||||
return strings.Join(output, "\n")
|
|
||||||
}
|
|
||||||
|
|
@ -1,235 +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 vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
func validateControlFlow(code []byte, section int, metadata []*functionMetadata, jt *JumpTable) (int, error) {
|
|
||||||
var (
|
|
||||||
maxStackHeight = int(metadata[section].inputs)
|
|
||||||
visitCount = 0
|
|
||||||
next = make([]int, 0, 1)
|
|
||||||
)
|
|
||||||
var (
|
|
||||||
stackBoundsMax = make([]uint16, len(code))
|
|
||||||
stackBoundsMin = make([]uint16, len(code))
|
|
||||||
)
|
|
||||||
setBounds := func(pos, min, maxi int) {
|
|
||||||
// The stackboundMax slice is a bit peculiar. We use `0` to denote
|
|
||||||
// not set. Therefore, we use `1` to represent the value `0`, and so on.
|
|
||||||
// So if the caller wants to store `1` as max bound, we internally store it as
|
|
||||||
// `2`.
|
|
||||||
if stackBoundsMax[pos] == 0 { // Not yet set
|
|
||||||
visitCount++
|
|
||||||
}
|
|
||||||
if maxi < 65535 {
|
|
||||||
stackBoundsMax[pos] = uint16(maxi + 1)
|
|
||||||
}
|
|
||||||
stackBoundsMin[pos] = uint16(min)
|
|
||||||
maxStackHeight = max(maxStackHeight, maxi)
|
|
||||||
}
|
|
||||||
getStackMaxMin := func(pos int) (ok bool, min, max int) {
|
|
||||||
maxi := stackBoundsMax[pos]
|
|
||||||
if maxi == 0 { // Not yet set
|
|
||||||
return false, 0, 0
|
|
||||||
}
|
|
||||||
return true, int(stackBoundsMin[pos]), int(maxi - 1)
|
|
||||||
}
|
|
||||||
// set the initial stack bounds
|
|
||||||
setBounds(0, int(metadata[section].inputs), int(metadata[section].inputs))
|
|
||||||
|
|
||||||
qualifiedExit := false
|
|
||||||
for pos := 0; pos < len(code); pos++ {
|
|
||||||
op := OpCode(code[pos])
|
|
||||||
ok, currentStackMin, currentStackMax := getStackMaxMin(pos)
|
|
||||||
if !ok {
|
|
||||||
return 0, errUnreachableCode
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case CALLF:
|
|
||||||
arg, _ := parseUint16(code[pos+1:])
|
|
||||||
newSection := metadata[arg]
|
|
||||||
if err := newSection.checkInputs(currentStackMin); err != nil {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
|
||||||
}
|
|
||||||
if err := newSection.checkStackMax(currentStackMax); err != nil {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
|
||||||
}
|
|
||||||
delta := newSection.stackDelta()
|
|
||||||
currentStackMax += delta
|
|
||||||
currentStackMin += delta
|
|
||||||
case RETF:
|
|
||||||
/* From the spec:
|
|
||||||
> for RETF the following must hold: stack_height_max == stack_height_min == types[current_code_index].outputs,
|
|
||||||
|
|
||||||
In other words: RETF must unambiguously return all items remaining on the stack.
|
|
||||||
*/
|
|
||||||
if currentStackMax != currentStackMin {
|
|
||||||
return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", errInvalidOutputs, currentStackMax, currentStackMin, pos)
|
|
||||||
}
|
|
||||||
numOutputs := int(metadata[section].outputs)
|
|
||||||
if numOutputs >= maxOutputItems {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", errInvalidNonReturningFlag, pos)
|
|
||||||
}
|
|
||||||
if numOutputs != currentStackMin {
|
|
||||||
return 0, fmt.Errorf("%w: have %d, want %d, at pos %d", errInvalidOutputs, numOutputs, currentStackMin, pos)
|
|
||||||
}
|
|
||||||
qualifiedExit = true
|
|
||||||
case JUMPF:
|
|
||||||
arg, _ := parseUint16(code[pos+1:])
|
|
||||||
newSection := metadata[arg]
|
|
||||||
|
|
||||||
if err := newSection.checkStackMax(currentStackMax); err != nil {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
if newSection.outputs == 0x80 {
|
|
||||||
if err := newSection.checkInputs(currentStackMin); err != nil {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", err, pos)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if currentStackMax != currentStackMin {
|
|
||||||
return 0, fmt.Errorf("%w: max %d, min %d, at pos %d", errInvalidOutputs, currentStackMax, currentStackMin, pos)
|
|
||||||
}
|
|
||||||
wantStack := int(metadata[section].outputs) - newSection.stackDelta()
|
|
||||||
if currentStackMax != wantStack {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", errInvalidOutputs, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qualifiedExit = qualifiedExit || newSection.outputs < maxOutputItems
|
|
||||||
case DUPN:
|
|
||||||
arg := int(code[pos+1]) + 1
|
|
||||||
if want, have := arg, currentStackMin; want > have {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
|
||||||
}
|
|
||||||
case SWAPN:
|
|
||||||
arg := int(code[pos+1]) + 1
|
|
||||||
if want, have := arg+1, currentStackMin; want > have {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
|
||||||
}
|
|
||||||
case EXCHANGE:
|
|
||||||
arg := int(code[pos+1])
|
|
||||||
n := arg>>4 + 1
|
|
||||||
m := arg&0x0f + 1
|
|
||||||
if want, have := n+m+1, currentStackMin; want > have {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if want, have := jt[op].minStack, currentStackMin; want > have {
|
|
||||||
return 0, fmt.Errorf("%w: at pos %d", ErrStackUnderflow{stackLen: have, required: want}, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !terminals[op] && op != CALLF {
|
|
||||||
change := int(params.StackLimit) - jt[op].maxStack
|
|
||||||
currentStackMax += change
|
|
||||||
currentStackMin += change
|
|
||||||
}
|
|
||||||
next = next[:0]
|
|
||||||
switch op {
|
|
||||||
case RJUMP:
|
|
||||||
nextPos := pos + 2 + parseInt16(code[pos+1:])
|
|
||||||
next = append(next, nextPos)
|
|
||||||
// We set the stack bounds of the destination
|
|
||||||
// and skip the argument, only for RJUMP, all other opcodes are handled later
|
|
||||||
if nextPos+1 < pos {
|
|
||||||
ok, nextMin, nextMax := getStackMaxMin(nextPos + 1)
|
|
||||||
if !ok {
|
|
||||||
return 0, errInvalidBackwardJump
|
|
||||||
}
|
|
||||||
if nextMax != currentStackMax || nextMin != currentStackMin {
|
|
||||||
return 0, errInvalidMaxStackHeight
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok, nextMin, nextMax := getStackMaxMin(nextPos + 1)
|
|
||||||
if !ok {
|
|
||||||
setBounds(nextPos+1, currentStackMin, currentStackMax)
|
|
||||||
} else {
|
|
||||||
setBounds(nextPos+1, min(nextMin, currentStackMin), max(nextMax, currentStackMax))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case RJUMPI:
|
|
||||||
arg := parseInt16(code[pos+1:])
|
|
||||||
next = append(next, pos+2)
|
|
||||||
next = append(next, pos+2+arg)
|
|
||||||
case RJUMPV:
|
|
||||||
count := int(code[pos+1]) + 1
|
|
||||||
next = append(next, pos+1+2*count)
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
arg := parseInt16(code[pos+2+2*i:])
|
|
||||||
next = append(next, pos+1+2*count+arg)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if imm := int(immediates[op]); imm != 0 {
|
|
||||||
next = append(next, pos+imm)
|
|
||||||
} else {
|
|
||||||
// Simple op, no operand.
|
|
||||||
next = append(next, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if op != RJUMP && !terminals[op] {
|
|
||||||
for _, instr := range next {
|
|
||||||
nextPC := instr + 1
|
|
||||||
if nextPC >= len(code) {
|
|
||||||
return 0, fmt.Errorf("%w: end with %s, pos %d", errInvalidCodeTermination, op, pos)
|
|
||||||
}
|
|
||||||
if nextPC > pos {
|
|
||||||
// target reached via forward jump or seq flow
|
|
||||||
ok, nextMin, nextMax := getStackMaxMin(nextPC)
|
|
||||||
if !ok {
|
|
||||||
setBounds(nextPC, currentStackMin, currentStackMax)
|
|
||||||
} else {
|
|
||||||
setBounds(nextPC, min(nextMin, currentStackMin), max(nextMax, currentStackMax))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// target reached via backwards jump
|
|
||||||
ok, nextMin, nextMax := getStackMaxMin(nextPC)
|
|
||||||
if !ok {
|
|
||||||
return 0, errInvalidBackwardJump
|
|
||||||
}
|
|
||||||
if currentStackMax != nextMax {
|
|
||||||
return 0, fmt.Errorf("%w want %d as current max got %d at pos %d,", errInvalidBackwardJump, currentStackMax, nextMax, pos)
|
|
||||||
}
|
|
||||||
if currentStackMin != nextMin {
|
|
||||||
return 0, fmt.Errorf("%w want %d as current min got %d at pos %d,", errInvalidBackwardJump, currentStackMin, nextMin, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if op == RJUMP {
|
|
||||||
pos += 2 // skip the immediate
|
|
||||||
} else {
|
|
||||||
pos = next[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if qualifiedExit != (metadata[section].outputs < maxOutputItems) {
|
|
||||||
return 0, fmt.Errorf("%w no RETF or qualified JUMPF", errInvalidNonReturningFlag)
|
|
||||||
}
|
|
||||||
if maxStackHeight >= int(params.StackLimit) {
|
|
||||||
return 0, ErrStackOverflow{maxStackHeight, int(params.StackLimit)}
|
|
||||||
}
|
|
||||||
if maxStackHeight != int(metadata[section].maxStackHeight) {
|
|
||||||
return 0, fmt.Errorf("%w in code section %d: have %d, want %d", errInvalidMaxStackHeight, section, maxStackHeight, metadata[section].maxStackHeight)
|
|
||||||
}
|
|
||||||
return visitCount, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +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 vm
|
|
||||||
|
|
||||||
// immediate denotes how many immediate bytes an operation uses. This information
|
|
||||||
// is not required during runtime, only during EOF-validation, so is not
|
|
||||||
// places into the op-struct in the instruction table.
|
|
||||||
// Note: the immediates is fork-agnostic, and assumes that validity of opcodes at
|
|
||||||
// the given time is performed elsewhere.
|
|
||||||
var immediates [256]uint8
|
|
||||||
|
|
||||||
// terminals denotes whether instructions can be the final opcode in a code section.
|
|
||||||
// Note: the terminals is fork-agnostic, and assumes that validity of opcodes at
|
|
||||||
// the given time is performed elsewhere.
|
|
||||||
var terminals [256]bool
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// The legacy pushes
|
|
||||||
for i := uint8(1); i < 33; i++ {
|
|
||||||
immediates[int(PUSH0)+int(i)] = i
|
|
||||||
}
|
|
||||||
// And new eof opcodes.
|
|
||||||
immediates[DATALOADN] = 2
|
|
||||||
immediates[RJUMP] = 2
|
|
||||||
immediates[RJUMPI] = 2
|
|
||||||
immediates[RJUMPV] = 3
|
|
||||||
immediates[CALLF] = 2
|
|
||||||
immediates[JUMPF] = 2
|
|
||||||
immediates[DUPN] = 1
|
|
||||||
immediates[SWAPN] = 1
|
|
||||||
immediates[EXCHANGE] = 1
|
|
||||||
immediates[EOFCREATE] = 1
|
|
||||||
immediates[RETURNCONTRACT] = 1
|
|
||||||
|
|
||||||
// Define the terminals.
|
|
||||||
terminals[STOP] = true
|
|
||||||
terminals[RETF] = true
|
|
||||||
terminals[JUMPF] = true
|
|
||||||
terminals[RETURNCONTRACT] = true
|
|
||||||
terminals[RETURN] = true
|
|
||||||
terminals[REVERT] = true
|
|
||||||
terminals[INVALID] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Immediates returns the number bytes of immediates (argument not from
|
|
||||||
// stack but from code) a given opcode has.
|
|
||||||
// OBS:
|
|
||||||
// - This function assumes EOF instruction-set. It cannot be upon in
|
|
||||||
// a. pre-EOF code
|
|
||||||
// b. post-EOF but legacy code
|
|
||||||
// - RJUMPV is unique as it has a variable sized operand. The total size is
|
|
||||||
// determined by the count byte which immediately follows RJUMPV. This method
|
|
||||||
// will return '3' for RJUMPV, which is the minimum.
|
|
||||||
func Immediates(op OpCode) int {
|
|
||||||
return int(immediates[op])
|
|
||||||
}
|
|
||||||
|
|
@ -1,112 +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 vm
|
|
||||||
|
|
||||||
// opRjump implements the RJUMP opcode.
|
|
||||||
func opRjump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opRjumpi implements the RJUMPI opcode
|
|
||||||
func opRjumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opRjumpv implements the RJUMPV opcode
|
|
||||||
func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opCallf implements the CALLF opcode
|
|
||||||
func opCallf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opRetf implements the RETF opcode
|
|
||||||
func opRetf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opJumpf implements the JUMPF opcode
|
|
||||||
func opJumpf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opEOFCreate implements the EOFCREATE opcode
|
|
||||||
func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opReturnContract implements the RETURNCONTRACT opcode
|
|
||||||
func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opDataLoad implements the DATALOAD opcode
|
|
||||||
func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opDataLoadN implements the DATALOADN opcode
|
|
||||||
func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opDataSize implements the DATASIZE opcode
|
|
||||||
func opDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opDataCopy implements the DATACOPY opcode
|
|
||||||
func opDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opDupN implements the DUPN opcode
|
|
||||||
func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opSwapN implements the SWAPN opcode
|
|
||||||
func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opExchange implements the EXCHANGE opcode
|
|
||||||
func opExchange(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opReturnDataLoad implements the RETURNDATALOAD opcode
|
|
||||||
func opReturnDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opExtCall implements the EOFCREATE opcode
|
|
||||||
func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opExtDelegateCall implements the EXTDELEGATECALL opcode
|
|
||||||
func opExtDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// opExtStaticCall implements the EXTSTATICCALL opcode
|
|
||||||
func opExtStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +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 vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEOFMarshaling(t *testing.T) {
|
|
||||||
for i, test := range []struct {
|
|
||||||
want Container
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
want: Container{
|
|
||||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
|
||||||
data: []byte{0x01, 0x02, 0x03},
|
|
||||||
dataSize: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
want: Container{
|
|
||||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
|
||||||
data: []byte{0x01, 0x02, 0x03},
|
|
||||||
dataSize: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
want: Container{
|
|
||||||
types: []*functionMetadata{
|
|
||||||
{inputs: 0, outputs: 0x80, maxStackHeight: 1},
|
|
||||||
{inputs: 2, outputs: 3, maxStackHeight: 4},
|
|
||||||
{inputs: 1, outputs: 1, maxStackHeight: 1},
|
|
||||||
},
|
|
||||||
codeSections: [][]byte{
|
|
||||||
common.Hex2Bytes("604200"),
|
|
||||||
common.Hex2Bytes("6042604200"),
|
|
||||||
common.Hex2Bytes("00"),
|
|
||||||
},
|
|
||||||
data: []byte{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
var (
|
|
||||||
b = test.want.MarshalBinary()
|
|
||||||
got Container
|
|
||||||
)
|
|
||||||
t.Logf("b: %#x", b)
|
|
||||||
if err := got.UnmarshalBinary(b, true); err != nil && err != test.err {
|
|
||||||
t.Fatalf("test %d: got error \"%v\", want \"%v\"", i, err, test.err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, test.want) {
|
|
||||||
t.Fatalf("test %d: got %+v, want %+v", i, got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEOFSubcontainer(t *testing.T) {
|
|
||||||
var subcontainer = new(Container)
|
|
||||||
if err := subcontainer.UnmarshalBinary(common.Hex2Bytes("ef000101000402000100010400000000800000fe"), true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container := Container{
|
|
||||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
|
||||||
subContainers: []*Container{subcontainer},
|
|
||||||
data: []byte{0x01, 0x02, 0x03},
|
|
||||||
dataSize: 3,
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
b = container.MarshalBinary()
|
|
||||||
got Container
|
|
||||||
)
|
|
||||||
if err := got.UnmarshalBinary(b, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if res := got.MarshalBinary(); !reflect.DeepEqual(res, b) {
|
|
||||||
t.Fatalf("invalid marshalling, want %v got %v", b, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshaling(t *testing.T) {
|
|
||||||
tests := []string{
|
|
||||||
"EF000101000402000100040400000000800000E0000000",
|
|
||||||
"ef0001010004020001000d04000000008000025fe100055f5fe000035f600100",
|
|
||||||
}
|
|
||||||
for i, test := range tests {
|
|
||||||
s, err := hex.DecodeString(test)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("test %d: error decoding: %v", i, err)
|
|
||||||
}
|
|
||||||
var got Container
|
|
||||||
if err := got.UnmarshalBinary(s, true); err != nil {
|
|
||||||
t.Fatalf("test %d: got error %v", i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,255 +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 vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Below are all possible errors that can occur during validation of
|
|
||||||
// EOF containers.
|
|
||||||
var (
|
|
||||||
errInvalidMagic = errors.New("invalid magic")
|
|
||||||
errUndefinedInstruction = errors.New("undefined instruction")
|
|
||||||
errTruncatedImmediate = errors.New("truncated immediate")
|
|
||||||
errInvalidSectionArgument = errors.New("invalid section argument")
|
|
||||||
errInvalidCallArgument = errors.New("callf into non-returning section")
|
|
||||||
errInvalidDataloadNArgument = errors.New("invalid dataloadN argument")
|
|
||||||
errInvalidJumpDest = errors.New("invalid jump destination")
|
|
||||||
errInvalidBackwardJump = errors.New("invalid backward jump")
|
|
||||||
errInvalidOutputs = errors.New("invalid number of outputs")
|
|
||||||
errInvalidMaxStackHeight = errors.New("invalid max stack height")
|
|
||||||
errInvalidCodeTermination = errors.New("invalid code termination")
|
|
||||||
errEOFCreateWithTruncatedSection = errors.New("eofcreate with truncated section")
|
|
||||||
errOrphanedSubcontainer = errors.New("subcontainer not referenced at all")
|
|
||||||
errIncompatibleContainerKind = errors.New("incompatible container kind")
|
|
||||||
errStopAndReturnContract = errors.New("Stop/Return and Returncontract in the same code section")
|
|
||||||
errStopInInitCode = errors.New("initcode contains a RETURN or STOP opcode")
|
|
||||||
errTruncatedTopLevelContainer = errors.New("truncated top level container")
|
|
||||||
errUnreachableCode = errors.New("unreachable code")
|
|
||||||
errInvalidNonReturningFlag = errors.New("invalid non-returning flag, bad RETF")
|
|
||||||
errInvalidVersion = errors.New("invalid version")
|
|
||||||
errMissingTypeHeader = errors.New("missing type header")
|
|
||||||
errInvalidTypeSize = errors.New("invalid type section size")
|
|
||||||
errMissingCodeHeader = errors.New("missing code header")
|
|
||||||
errInvalidCodeSize = errors.New("invalid code size")
|
|
||||||
errInvalidContainerSectionSize = errors.New("invalid container section size")
|
|
||||||
errMissingDataHeader = errors.New("missing data header")
|
|
||||||
errMissingTerminator = errors.New("missing header terminator")
|
|
||||||
errTooManyInputs = errors.New("invalid type content, too many inputs")
|
|
||||||
errTooManyOutputs = errors.New("invalid type content, too many outputs")
|
|
||||||
errInvalidSection0Type = errors.New("invalid section 0 type, input and output should be zero and non-returning (0x80)")
|
|
||||||
errTooLargeMaxStackHeight = errors.New("invalid type content, max stack height exceeds limit")
|
|
||||||
errInvalidContainerSize = errors.New("invalid container size")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
notRefByEither = iota
|
|
||||||
refByReturnContract
|
|
||||||
refByEOFCreate
|
|
||||||
)
|
|
||||||
|
|
||||||
type validationResult struct {
|
|
||||||
visitedCode map[int]struct{}
|
|
||||||
visitedSubContainers map[int]int
|
|
||||||
isInitCode bool
|
|
||||||
isRuntime bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateCode validates the code parameter against the EOF v1 validity requirements.
|
|
||||||
func validateCode(code []byte, section int, container *Container, jt *JumpTable, isInitCode bool) (*validationResult, error) {
|
|
||||||
var (
|
|
||||||
i = 0
|
|
||||||
// Tracks the number of actual instructions in the code (e.g.
|
|
||||||
// non-immediate values). This is used at the end to determine
|
|
||||||
// if each instruction is reachable.
|
|
||||||
count = 0
|
|
||||||
op OpCode
|
|
||||||
analysis bitvec
|
|
||||||
visitedCode map[int]struct{}
|
|
||||||
visitedSubcontainers map[int]int
|
|
||||||
hasReturnContract bool
|
|
||||||
hasStop bool
|
|
||||||
)
|
|
||||||
// This loop visits every single instruction and verifies:
|
|
||||||
// * if the instruction is valid for the given jump table.
|
|
||||||
// * if the instruction has an immediate value, it is not truncated.
|
|
||||||
// * if performing a relative jump, all jump destinations are valid.
|
|
||||||
// * if changing code sections, the new code section index is valid and
|
|
||||||
// will not cause a stack overflow.
|
|
||||||
for i < len(code) {
|
|
||||||
count++
|
|
||||||
op = OpCode(code[i])
|
|
||||||
if jt[op].undefined {
|
|
||||||
return nil, fmt.Errorf("%w: op %s, pos %d", errUndefinedInstruction, op, i)
|
|
||||||
}
|
|
||||||
size := int(immediates[op])
|
|
||||||
if size != 0 && len(code) <= i+size {
|
|
||||||
return nil, fmt.Errorf("%w: op %s, pos %d", errTruncatedImmediate, op, i)
|
|
||||||
}
|
|
||||||
switch op {
|
|
||||||
case RJUMP, RJUMPI:
|
|
||||||
if err := checkDest(code, &analysis, i+1, i+3, len(code)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case RJUMPV:
|
|
||||||
maxSize := int(code[i+1])
|
|
||||||
length := maxSize + 1
|
|
||||||
if len(code) <= i+length {
|
|
||||||
return nil, fmt.Errorf("%w: jump table truncated, op %s, pos %d", errTruncatedImmediate, op, i)
|
|
||||||
}
|
|
||||||
offset := i + 2
|
|
||||||
for j := 0; j < length; j++ {
|
|
||||||
if err := checkDest(code, &analysis, offset+j*2, offset+(length*2), len(code)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 2 * maxSize
|
|
||||||
case CALLF:
|
|
||||||
arg, _ := parseUint16(code[i+1:])
|
|
||||||
if arg >= len(container.types) {
|
|
||||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.types), i)
|
|
||||||
}
|
|
||||||
if container.types[arg].outputs == 0x80 {
|
|
||||||
return nil, fmt.Errorf("%w: section %v", errInvalidCallArgument, arg)
|
|
||||||
}
|
|
||||||
if visitedCode == nil {
|
|
||||||
visitedCode = make(map[int]struct{})
|
|
||||||
}
|
|
||||||
visitedCode[arg] = struct{}{}
|
|
||||||
case JUMPF:
|
|
||||||
arg, _ := parseUint16(code[i+1:])
|
|
||||||
if arg >= len(container.types) {
|
|
||||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidSectionArgument, arg, len(container.types), i)
|
|
||||||
}
|
|
||||||
if container.types[arg].outputs != 0x80 && container.types[arg].outputs > container.types[section].outputs {
|
|
||||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidOutputs, arg, len(container.types), i)
|
|
||||||
}
|
|
||||||
if visitedCode == nil {
|
|
||||||
visitedCode = make(map[int]struct{})
|
|
||||||
}
|
|
||||||
visitedCode[arg] = struct{}{}
|
|
||||||
case DATALOADN:
|
|
||||||
arg, _ := parseUint16(code[i+1:])
|
|
||||||
// TODO why are we checking this? We should just pad
|
|
||||||
if arg+32 > len(container.data) {
|
|
||||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, len(container.data), i)
|
|
||||||
}
|
|
||||||
case RETURNCONTRACT:
|
|
||||||
if !isInitCode {
|
|
||||||
return nil, errIncompatibleContainerKind
|
|
||||||
}
|
|
||||||
arg := int(code[i+1])
|
|
||||||
if arg >= len(container.subContainers) {
|
|
||||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i)
|
|
||||||
}
|
|
||||||
if visitedSubcontainers == nil {
|
|
||||||
visitedSubcontainers = make(map[int]int)
|
|
||||||
}
|
|
||||||
// We need to store per subcontainer how it was referenced
|
|
||||||
if v, ok := visitedSubcontainers[arg]; ok && v != refByReturnContract {
|
|
||||||
return nil, fmt.Errorf("section already referenced, arg :%d", arg)
|
|
||||||
}
|
|
||||||
if hasStop {
|
|
||||||
return nil, errStopAndReturnContract
|
|
||||||
}
|
|
||||||
hasReturnContract = true
|
|
||||||
visitedSubcontainers[arg] = refByReturnContract
|
|
||||||
case EOFCREATE:
|
|
||||||
arg := int(code[i+1])
|
|
||||||
if arg >= len(container.subContainers) {
|
|
||||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i)
|
|
||||||
}
|
|
||||||
if ct := container.subContainers[arg]; len(ct.data) != ct.dataSize {
|
|
||||||
return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, len(ct.data), ct.dataSize, i)
|
|
||||||
}
|
|
||||||
if visitedSubcontainers == nil {
|
|
||||||
visitedSubcontainers = make(map[int]int)
|
|
||||||
}
|
|
||||||
// We need to store per subcontainer how it was referenced
|
|
||||||
if v, ok := visitedSubcontainers[arg]; ok && v != refByEOFCreate {
|
|
||||||
return nil, fmt.Errorf("section already referenced, arg :%d", arg)
|
|
||||||
}
|
|
||||||
visitedSubcontainers[arg] = refByEOFCreate
|
|
||||||
case STOP, RETURN:
|
|
||||||
if isInitCode {
|
|
||||||
return nil, errStopInInitCode
|
|
||||||
}
|
|
||||||
if hasReturnContract {
|
|
||||||
return nil, errStopAndReturnContract
|
|
||||||
}
|
|
||||||
hasStop = true
|
|
||||||
}
|
|
||||||
i += size + 1
|
|
||||||
}
|
|
||||||
// Code sections may not "fall through" and require proper termination.
|
|
||||||
// Therefore, the last instruction must be considered terminal or RJUMP.
|
|
||||||
if !terminals[op] && op != RJUMP {
|
|
||||||
return nil, fmt.Errorf("%w: end with %s, pos %d", errInvalidCodeTermination, op, i)
|
|
||||||
}
|
|
||||||
if paths, err := validateControlFlow(code, section, container.types, jt); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if paths != count {
|
|
||||||
// TODO(matt): return actual position of unreachable code
|
|
||||||
return nil, errUnreachableCode
|
|
||||||
}
|
|
||||||
return &validationResult{
|
|
||||||
visitedCode: visitedCode,
|
|
||||||
visitedSubContainers: visitedSubcontainers,
|
|
||||||
isInitCode: hasReturnContract,
|
|
||||||
isRuntime: hasStop,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkDest parses a relative offset at code[0:2] and checks if it is a valid jump destination.
|
|
||||||
func checkDest(code []byte, analysis *bitvec, imm, from, length int) error {
|
|
||||||
if len(code) < imm+2 {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if analysis != nil && *analysis == nil {
|
|
||||||
*analysis = eofCodeBitmap(code)
|
|
||||||
}
|
|
||||||
offset := parseInt16(code[imm:])
|
|
||||||
dest := from + offset
|
|
||||||
if dest < 0 || dest >= length {
|
|
||||||
return fmt.Errorf("%w: out-of-bounds offset: offset %d, dest %d, pos %d", errInvalidJumpDest, offset, dest, imm)
|
|
||||||
}
|
|
||||||
if !analysis.codeSegment(uint64(dest)) {
|
|
||||||
return fmt.Errorf("%w: offset into immediate: offset %d, dest %d, pos %d", errInvalidJumpDest, offset, dest, imm)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//// disasm is a helper utility to show a sequence of comma-separated operations,
|
|
||||||
//// with immediates shown inline,
|
|
||||||
//// e.g: PUSH1(0x00),EOFCREATE(0x00),
|
|
||||||
//func disasm(code []byte) string {
|
|
||||||
// var ops []string
|
|
||||||
// for i := 0; i < len(code); i++ {
|
|
||||||
// var op string
|
|
||||||
// if args := immediates[code[i]]; args > 0 {
|
|
||||||
// op = fmt.Sprintf("%v(%#x)", OpCode(code[i]).String(), code[i+1:i+1+int(args)])
|
|
||||||
// i += int(args)
|
|
||||||
// } else {
|
|
||||||
// op = OpCode(code[i]).String()
|
|
||||||
// }
|
|
||||||
// ops = append(ops, op)
|
|
||||||
// }
|
|
||||||
// return strings.Join(ops, ",")
|
|
||||||
//}
|
|
||||||
|
|
@ -1,517 +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 vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValidateCode(t *testing.T) {
|
|
||||||
for i, test := range []struct {
|
|
||||||
code []byte
|
|
||||||
section int
|
|
||||||
metadata []*functionMetadata
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(CALLER),
|
|
||||||
byte(POP),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(CALLF), 0x00, 0x00,
|
|
||||||
byte(RETF),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 0}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(ADDRESS),
|
|
||||||
byte(CALLF), 0x00, 0x00,
|
|
||||||
byte(POP),
|
|
||||||
byte(RETF),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 1}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(CALLER),
|
|
||||||
byte(POP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
err: errInvalidCodeTermination,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(RJUMP),
|
|
||||||
byte(0x00),
|
|
||||||
byte(0x01),
|
|
||||||
byte(CALLER),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 0}},
|
|
||||||
err: errUnreachableCode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH1),
|
|
||||||
byte(0x42),
|
|
||||||
byte(ADD),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
err: ErrStackUnderflow{stackLen: 1, required: 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH1),
|
|
||||||
byte(0x42),
|
|
||||||
byte(POP),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 2}},
|
|
||||||
err: errInvalidMaxStackHeight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPI),
|
|
||||||
byte(0x00),
|
|
||||||
byte(0x01),
|
|
||||||
byte(PUSH1),
|
|
||||||
byte(0x42), // jumps to here
|
|
||||||
byte(POP),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
err: errInvalidJumpDest,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPV),
|
|
||||||
byte(0x01),
|
|
||||||
byte(0x00),
|
|
||||||
byte(0x01),
|
|
||||||
byte(0x00),
|
|
||||||
byte(0x02),
|
|
||||||
byte(PUSH1),
|
|
||||||
byte(0x42), // jumps to here
|
|
||||||
byte(POP), // and here
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
err: errInvalidJumpDest,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPV),
|
|
||||||
byte(0x00),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
err: errTruncatedImmediate,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(RJUMP), 0x00, 0x03,
|
|
||||||
byte(JUMPDEST), // this code is unreachable to forward jumps alone
|
|
||||||
byte(JUMPDEST),
|
|
||||||
byte(RETURN),
|
|
||||||
byte(PUSH1), 20,
|
|
||||||
byte(PUSH1), 39,
|
|
||||||
byte(PUSH1), 0x00,
|
|
||||||
byte(DATACOPY),
|
|
||||||
byte(PUSH1), 20,
|
|
||||||
byte(PUSH1), 0x00,
|
|
||||||
byte(RJUMP), 0xff, 0xef,
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}},
|
|
||||||
err: errUnreachableCode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH1), 1,
|
|
||||||
byte(RJUMPI), 0x00, 0x03,
|
|
||||||
byte(JUMPDEST),
|
|
||||||
byte(JUMPDEST),
|
|
||||||
byte(STOP),
|
|
||||||
byte(PUSH1), 20,
|
|
||||||
byte(PUSH1), 39,
|
|
||||||
byte(PUSH1), 0x00,
|
|
||||||
byte(DATACOPY),
|
|
||||||
byte(PUSH1), 20,
|
|
||||||
byte(PUSH1), 0x00,
|
|
||||||
byte(RETURN),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(PUSH1), 1,
|
|
||||||
byte(RJUMPV), 0x01, 0x00, 0x03, 0xff, 0xf8,
|
|
||||||
byte(JUMPDEST),
|
|
||||||
byte(JUMPDEST),
|
|
||||||
byte(STOP),
|
|
||||||
byte(PUSH1), 20,
|
|
||||||
byte(PUSH1), 39,
|
|
||||||
byte(PUSH1), 0x00,
|
|
||||||
byte(DATACOPY),
|
|
||||||
byte(PUSH1), 20,
|
|
||||||
byte(PUSH1), 0x00,
|
|
||||||
byte(RETURN),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 3}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(STOP),
|
|
||||||
byte(STOP),
|
|
||||||
byte(INVALID),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 0}},
|
|
||||||
err: errUnreachableCode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(RETF),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 1, maxStackHeight: 0}},
|
|
||||||
err: errInvalidOutputs,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(RETF),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 3, outputs: 3, maxStackHeight: 3}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(CALLF), 0x00, 0x01,
|
|
||||||
byte(POP),
|
|
||||||
byte(STOP),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}, {inputs: 0, outputs: 1, maxStackHeight: 0}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: []byte{
|
|
||||||
byte(ORIGIN),
|
|
||||||
byte(ORIGIN),
|
|
||||||
byte(CALLF), 0x00, 0x01,
|
|
||||||
byte(POP),
|
|
||||||
byte(RETF),
|
|
||||||
},
|
|
||||||
section: 0,
|
|
||||||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 2}, {inputs: 2, outputs: 1, maxStackHeight: 2}},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
container := &Container{
|
|
||||||
types: test.metadata,
|
|
||||||
data: make([]byte, 0),
|
|
||||||
subContainers: make([]*Container, 0),
|
|
||||||
}
|
|
||||||
_, err := validateCode(test.code, test.section, container, &eofInstructionSet, false)
|
|
||||||
if !errors.Is(err, test.err) {
|
|
||||||
t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkRJUMPI tries to benchmark the RJUMPI opcode validation
|
|
||||||
// For this we do a bunch of RJUMPIs that jump backwards (in a potential infinite loop).
|
|
||||||
func BenchmarkRJUMPI(b *testing.B) {
|
|
||||||
snippet := []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPI), 0xFF, 0xFC,
|
|
||||||
}
|
|
||||||
code := []byte{}
|
|
||||||
for i := 0; i < params.MaxCodeSize/len(snippet)-1; i++ {
|
|
||||||
code = append(code, snippet...)
|
|
||||||
}
|
|
||||||
code = append(code, byte(STOP))
|
|
||||||
container := &Container{
|
|
||||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
data: make([]byte, 0),
|
|
||||||
subContainers: make([]*Container, 0),
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := validateCode(code, 0, container, &eofInstructionSet, false)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkRJUMPV tries to benchmark the validation of the RJUMPV opcode
|
|
||||||
// for this we set up as many RJUMPV opcodes with a full jumptable (containing 0s) as possible.
|
|
||||||
func BenchmarkRJUMPV(b *testing.B) {
|
|
||||||
snippet := []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPV),
|
|
||||||
0xff, // count
|
|
||||||
0x00, 0x00,
|
|
||||||
}
|
|
||||||
for i := 0; i < 255; i++ {
|
|
||||||
snippet = append(snippet, []byte{0x00, 0x00}...)
|
|
||||||
}
|
|
||||||
code := []byte{}
|
|
||||||
for i := 0; i < 24576/len(snippet)-1; i++ {
|
|
||||||
code = append(code, snippet...)
|
|
||||||
}
|
|
||||||
code = append(code, byte(PUSH0))
|
|
||||||
code = append(code, byte(STOP))
|
|
||||||
container := &Container{
|
|
||||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
data: make([]byte, 0),
|
|
||||||
subContainers: make([]*Container, 0),
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := validateCode(code, 0, container, &pragueInstructionSet, false)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkEOFValidation tries to benchmark the code validation for the CALLF/RETF operation.
|
|
||||||
// For this we set up code that calls into 1024 code sections which can either
|
|
||||||
// - just contain a RETF opcode
|
|
||||||
// - or code to again call into 1024 code sections.
|
|
||||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
|
||||||
func BenchmarkEOFValidation(b *testing.B) {
|
|
||||||
var container Container
|
|
||||||
var code []byte
|
|
||||||
maxSections := 1024
|
|
||||||
for i := 0; i < maxSections; i++ {
|
|
||||||
code = append(code, byte(CALLF))
|
|
||||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
|
||||||
}
|
|
||||||
// First container
|
|
||||||
container.codeSections = append(container.codeSections, append(code, byte(STOP)))
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
|
||||||
|
|
||||||
inner := []byte{
|
|
||||||
byte(RETF),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 1023; i++ {
|
|
||||||
container.codeSections = append(container.codeSections, inner)
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 12; i++ {
|
|
||||||
container.codeSections[i+1] = append(code, byte(RETF))
|
|
||||||
}
|
|
||||||
|
|
||||||
bin := container.MarshalBinary()
|
|
||||||
if len(bin) > 48*1024 {
|
|
||||||
b.Fatal("Exceeds 48Kb")
|
|
||||||
}
|
|
||||||
|
|
||||||
var container2 Container
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if err := container2.UnmarshalBinary(bin, true); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkEOFValidation2 tries to benchmark the code validation for the CALLF/RETF operation.
|
|
||||||
// For this we set up code that calls into 1024 code sections which
|
|
||||||
// - contain calls to some other code sections.
|
|
||||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
|
||||||
func BenchmarkEOFValidation2(b *testing.B) {
|
|
||||||
var container Container
|
|
||||||
var code []byte
|
|
||||||
maxSections := 1024
|
|
||||||
for i := 0; i < maxSections; i++ {
|
|
||||||
code = append(code, byte(CALLF))
|
|
||||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
|
||||||
}
|
|
||||||
code = append(code, byte(STOP))
|
|
||||||
// First container
|
|
||||||
container.codeSections = append(container.codeSections, code)
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
|
||||||
|
|
||||||
inner := []byte{
|
|
||||||
byte(CALLF), 0x03, 0xE8,
|
|
||||||
byte(CALLF), 0x03, 0xE9,
|
|
||||||
byte(CALLF), 0x03, 0xF0,
|
|
||||||
byte(CALLF), 0x03, 0xF1,
|
|
||||||
byte(CALLF), 0x03, 0xF2,
|
|
||||||
byte(CALLF), 0x03, 0xF3,
|
|
||||||
byte(CALLF), 0x03, 0xF4,
|
|
||||||
byte(CALLF), 0x03, 0xF5,
|
|
||||||
byte(CALLF), 0x03, 0xF6,
|
|
||||||
byte(CALLF), 0x03, 0xF7,
|
|
||||||
byte(CALLF), 0x03, 0xF8,
|
|
||||||
byte(CALLF), 0x03, 0xF,
|
|
||||||
byte(RETF),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 1023; i++ {
|
|
||||||
container.codeSections = append(container.codeSections, inner)
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
|
||||||
}
|
|
||||||
|
|
||||||
bin := container.MarshalBinary()
|
|
||||||
if len(bin) > 48*1024 {
|
|
||||||
b.Fatal("Exceeds 48Kb")
|
|
||||||
}
|
|
||||||
|
|
||||||
var container2 Container
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if err := container2.UnmarshalBinary(bin, true); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkEOFValidation3 tries to benchmark the code validation for the CALLF/RETF and RJUMPI/V operations.
|
|
||||||
// For this we set up code that calls into 1024 code sections which either
|
|
||||||
// - contain an RJUMP opcode
|
|
||||||
// - contain calls to other code sections
|
|
||||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
|
||||||
func BenchmarkEOFValidation3(b *testing.B) {
|
|
||||||
var container Container
|
|
||||||
var code []byte
|
|
||||||
snippet := []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPV),
|
|
||||||
0xff, // count
|
|
||||||
0x00, 0x00,
|
|
||||||
}
|
|
||||||
for i := 0; i < 255; i++ {
|
|
||||||
snippet = append(snippet, []byte{0x00, 0x00}...)
|
|
||||||
}
|
|
||||||
code = append(code, snippet...)
|
|
||||||
// First container, calls into all other containers
|
|
||||||
maxSections := 1024
|
|
||||||
for i := 0; i < maxSections; i++ {
|
|
||||||
code = append(code, byte(CALLF))
|
|
||||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
|
||||||
}
|
|
||||||
code = append(code, byte(STOP))
|
|
||||||
container.codeSections = append(container.codeSections, code)
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1})
|
|
||||||
|
|
||||||
// Other containers
|
|
||||||
for i := 0; i < 1023; i++ {
|
|
||||||
container.codeSections = append(container.codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)})
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
|
||||||
}
|
|
||||||
// Other containers
|
|
||||||
for i := 0; i < 68; i++ {
|
|
||||||
container.codeSections[i+1] = append(snippet, byte(RETF))
|
|
||||||
container.types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1}
|
|
||||||
}
|
|
||||||
bin := container.MarshalBinary()
|
|
||||||
if len(bin) > 48*1024 {
|
|
||||||
b.Fatal("Exceeds 48Kb")
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
b.ReportMetric(float64(len(bin)), "bytes")
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for k := 0; k < 40; k++ {
|
|
||||||
var container2 Container
|
|
||||||
if err := container2.UnmarshalBinary(bin, true); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRJUMPI_2(b *testing.B) {
|
|
||||||
code := []byte{
|
|
||||||
byte(PUSH0),
|
|
||||||
byte(RJUMPI), 0xFF, 0xFC,
|
|
||||||
}
|
|
||||||
for i := 0; i < params.MaxCodeSize/4-1; i++ {
|
|
||||||
code = append(code, byte(PUSH0))
|
|
||||||
x := -4 * i
|
|
||||||
code = append(code, byte(RJUMPI))
|
|
||||||
code = binary.BigEndian.AppendUint16(code, uint16(x))
|
|
||||||
}
|
|
||||||
code = append(code, byte(STOP))
|
|
||||||
container := &Container{
|
|
||||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
|
||||||
data: make([]byte, 0),
|
|
||||||
subContainers: make([]*Container, 0),
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := validateCode(code, 0, container, &pragueInstructionSet, false)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FuzzUnmarshalBinary(f *testing.F) {
|
|
||||||
f.Fuzz(func(_ *testing.T, input []byte) {
|
|
||||||
var container Container
|
|
||||||
container.UnmarshalBinary(input, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func FuzzValidate(f *testing.F) {
|
|
||||||
f.Fuzz(func(_ *testing.T, code []byte, maxStack uint16) {
|
|
||||||
var container Container
|
|
||||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: maxStack})
|
|
||||||
validateCode(code, 0, &container, &pragueInstructionSet, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -485,20 +485,3 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func gasExtDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
func gasExtStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// gasEOFCreate returns the gas-cost for EOF-Create. Hashing charge needs to be
|
|
||||||
// deducted in the opcode itself, since it depends on the immediate
|
|
||||||
func gasEOFCreate(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ var (
|
||||||
cancunInstructionSet = newCancunInstructionSet()
|
cancunInstructionSet = newCancunInstructionSet()
|
||||||
verkleInstructionSet = newVerkleInstructionSet()
|
verkleInstructionSet = newVerkleInstructionSet()
|
||||||
pragueInstructionSet = newPragueInstructionSet()
|
pragueInstructionSet = newPragueInstructionSet()
|
||||||
eofInstructionSet = newEOFInstructionSetForTesting()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
|
|
@ -92,16 +91,6 @@ func newVerkleInstructionSet() JumpTable {
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEOFInstructionSetForTesting() JumpTable {
|
|
||||||
return newEOFInstructionSetForTesting()
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEOFInstructionSetForTesting() JumpTable {
|
|
||||||
instructionSet := newPragueInstructionSet()
|
|
||||||
enableEOF(&instructionSet)
|
|
||||||
return validate(instructionSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPragueInstructionSet() JumpTable {
|
func newPragueInstructionSet() JumpTable {
|
||||||
instructionSet := newCancunInstructionSet()
|
instructionSet := newCancunInstructionSet()
|
||||||
enable7702(&instructionSet) // EIP-7702 Setcode transaction type
|
enable7702(&instructionSet) // EIP-7702 Setcode transaction type
|
||||||
|
|
|
||||||
|
|
@ -120,19 +120,3 @@ func memoryRevert(stack *Stack) (uint64, bool) {
|
||||||
func memoryLog(stack *Stack) (uint64, bool) {
|
func memoryLog(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryExtCall(stack *Stack) (uint64, bool) {
|
|
||||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
func memoryDataCopy(stack *Stack) (uint64, bool) {
|
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
func memoryEOFCreate(stack *Stack) (uint64, bool) {
|
|
||||||
return calcMemSize64(stack.Back(2), stack.Back(3))
|
|
||||||
}
|
|
||||||
|
|
||||||
func memoryReturnContract(stack *Stack) (uint64, bool) {
|
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
107
core/vm/testdata/precompiles/modexp_eip7883.json
vendored
Normal file
107
core/vm/testdata/precompiles/modexp_eip7883.json
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b",
|
||||||
|
"Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc",
|
||||||
|
"Name": "nagydani-1-square",
|
||||||
|
"Gas": 500,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b",
|
||||||
|
"Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec",
|
||||||
|
"Name": "nagydani-1-qube",
|
||||||
|
"Gas": 500,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b",
|
||||||
|
"Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2",
|
||||||
|
"Name": "nagydani-1-pow0x10001",
|
||||||
|
"Gas": 682,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087",
|
||||||
|
"Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70",
|
||||||
|
"Name": "nagydani-2-square",
|
||||||
|
"Gas": 500,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087",
|
||||||
|
"Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f",
|
||||||
|
"Name": "nagydani-2-qube",
|
||||||
|
"Gas": 500,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087",
|
||||||
|
"Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2",
|
||||||
|
"Name": "nagydani-2-pow0x10001",
|
||||||
|
"Gas": 2730,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d",
|
||||||
|
"Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8",
|
||||||
|
"Name": "nagydani-3-square",
|
||||||
|
"Gas": 682,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d",
|
||||||
|
"Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e",
|
||||||
|
"Name": "nagydani-3-qube",
|
||||||
|
"Gas": 682,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d",
|
||||||
|
"Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76",
|
||||||
|
"Name": "nagydani-3-pow0x10001",
|
||||||
|
"Gas": 10922,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f",
|
||||||
|
"Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10",
|
||||||
|
"Name": "nagydani-4-square",
|
||||||
|
"Gas": 2730,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f",
|
||||||
|
"Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc",
|
||||||
|
"Name": "nagydani-4-qube",
|
||||||
|
"Gas": 2730,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f",
|
||||||
|
"Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963",
|
||||||
|
"Name": "nagydani-4-pow0x10001",
|
||||||
|
"Gas": 43690,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad",
|
||||||
|
"Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb",
|
||||||
|
"Name": "nagydani-5-square",
|
||||||
|
"Gas": 10922,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad",
|
||||||
|
"Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f",
|
||||||
|
"Name": "nagydani-5-qube",
|
||||||
|
"Gas": 10922,
|
||||||
|
"NoBenchmark": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad",
|
||||||
|
"Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500",
|
||||||
|
"Name": "nagydani-5-pow0x10001",
|
||||||
|
"Gas": 174762,
|
||||||
|
"NoBenchmark": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
// mul multiplies two 256-bit numbers in little-endian order.
|
||||||
|
// The inputs are (R1,R2,R3,R4) times (R5,R6,R7,R8)
|
||||||
|
// and the product is stored in (c0,c1,c2,c3,c4,c5,c6,c7).
|
||||||
|
// Note that the input registers (R1,R2,R3) are overwritten.
|
||||||
#define mul(c0,c1,c2,c3,c4,c5,c6,c7) \
|
#define mul(c0,c1,c2,c3,c4,c5,c6,c7) \
|
||||||
MUL R1, R5, c0 \
|
MUL R1, R5, c0 \
|
||||||
UMULH R1, R5, c1 \
|
UMULH R1, R5, c1 \
|
||||||
|
|
@ -16,54 +20,54 @@
|
||||||
UMULH R2, R5, R26 \
|
UMULH R2, R5, R26 \
|
||||||
MUL R2, R6, R0 \
|
MUL R2, R6, R0 \
|
||||||
ADDS R0, R26 \
|
ADDS R0, R26 \
|
||||||
UMULH R2, R6, R27 \
|
UMULH R2, R6, c6 \
|
||||||
MUL R2, R7, R0 \
|
MUL R2, R7, R0 \
|
||||||
ADCS R0, R27 \
|
ADCS R0, c6 \
|
||||||
UMULH R2, R7, R29 \
|
UMULH R2, R7, c7 \
|
||||||
MUL R2, R8, R0 \
|
MUL R2, R8, R0 \
|
||||||
ADCS R0, R29 \
|
ADCS R0, c7 \
|
||||||
UMULH R2, R8, c5 \
|
UMULH R2, R8, c5 \
|
||||||
ADCS ZR, c5 \
|
ADCS ZR, c5 \
|
||||||
ADDS R1, c1 \
|
ADDS R1, c1 \
|
||||||
ADCS R26, c2 \
|
ADCS R26, c2 \
|
||||||
ADCS R27, c3 \
|
ADCS c6, c3 \
|
||||||
ADCS R29, c4 \
|
ADCS c7, c4 \
|
||||||
ADCS ZR, c5 \
|
ADCS ZR, c5 \
|
||||||
\
|
\
|
||||||
MUL R3, R5, R1 \
|
MUL R3, R5, R1 \
|
||||||
UMULH R3, R5, R26 \
|
UMULH R3, R5, R26 \
|
||||||
MUL R3, R6, R0 \
|
MUL R3, R6, R0 \
|
||||||
ADDS R0, R26 \
|
ADDS R0, R26 \
|
||||||
UMULH R3, R6, R27 \
|
UMULH R3, R6, R2 \
|
||||||
MUL R3, R7, R0 \
|
MUL R3, R7, R0 \
|
||||||
ADCS R0, R27 \
|
ADCS R0, R2 \
|
||||||
UMULH R3, R7, R29 \
|
UMULH R3, R7, c7 \
|
||||||
MUL R3, R8, R0 \
|
MUL R3, R8, R0 \
|
||||||
ADCS R0, R29 \
|
ADCS R0, c7 \
|
||||||
UMULH R3, R8, c6 \
|
UMULH R3, R8, c6 \
|
||||||
ADCS ZR, c6 \
|
ADCS ZR, c6 \
|
||||||
ADDS R1, c2 \
|
ADDS R1, c2 \
|
||||||
ADCS R26, c3 \
|
ADCS R26, c3 \
|
||||||
ADCS R27, c4 \
|
ADCS R2, c4 \
|
||||||
ADCS R29, c5 \
|
ADCS c7, c5 \
|
||||||
ADCS ZR, c6 \
|
ADCS ZR, c6 \
|
||||||
\
|
\
|
||||||
MUL R4, R5, R1 \
|
MUL R4, R5, R1 \
|
||||||
UMULH R4, R5, R26 \
|
UMULH R4, R5, R26 \
|
||||||
MUL R4, R6, R0 \
|
MUL R4, R6, R0 \
|
||||||
ADDS R0, R26 \
|
ADDS R0, R26 \
|
||||||
UMULH R4, R6, R27 \
|
UMULH R4, R6, R2 \
|
||||||
MUL R4, R7, R0 \
|
MUL R4, R7, R0 \
|
||||||
ADCS R0, R27 \
|
ADCS R0, R2 \
|
||||||
UMULH R4, R7, R29 \
|
UMULH R4, R7, R3 \
|
||||||
MUL R4, R8, R0 \
|
MUL R4, R8, R0 \
|
||||||
ADCS R0, R29 \
|
ADCS R0, R3 \
|
||||||
UMULH R4, R8, c7 \
|
UMULH R4, R8, c7 \
|
||||||
ADCS ZR, c7 \
|
ADCS ZR, c7 \
|
||||||
ADDS R1, c3 \
|
ADDS R1, c3 \
|
||||||
ADCS R26, c4 \
|
ADCS R26, c4 \
|
||||||
ADCS R27, c5 \
|
ADCS R2, c5 \
|
||||||
ADCS R29, c6 \
|
ADCS R3, c6 \
|
||||||
ADCS ZR, c7
|
ADCS ZR, c7
|
||||||
|
|
||||||
#define gfpReduce() \
|
#define gfpReduce() \
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package bn256
|
package bn256
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bn254"
|
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||||
|
|
@ -31,21 +32,62 @@ func (g *G1) ScalarMult(a *G1, scalar *big.Int) {
|
||||||
|
|
||||||
// Unmarshal deserializes `buf` into `g`
|
// Unmarshal deserializes `buf` into `g`
|
||||||
//
|
//
|
||||||
// Note: whether the deserialization is of a compressed
|
// The input is expected to be in the EVM format:
|
||||||
// or an uncompressed point, is encoded in the bytes.
|
// 64 bytes: [32-byte x coordinate][32-byte y coordinate]
|
||||||
//
|
// where each coordinate is in big-endian format.
|
||||||
// For our purpose, the point will always be serialized
|
|
||||||
// as uncompressed, ie 64 bytes.
|
|
||||||
//
|
//
|
||||||
// This method also checks whether the point is on the
|
// This method also checks whether the point is on the
|
||||||
// curve and in the prime order subgroup.
|
// curve and in the prime order subgroup.
|
||||||
func (g *G1) Unmarshal(buf []byte) (int, error) {
|
func (g *G1) Unmarshal(buf []byte) (int, error) {
|
||||||
return g.inner.SetBytes(buf)
|
if len(buf) < 64 {
|
||||||
|
return 0, errors.New("invalid G1 point size")
|
||||||
|
}
|
||||||
|
|
||||||
|
if allZeroes(buf[:64]) {
|
||||||
|
// point at infinity
|
||||||
|
g.inner.X.SetZero()
|
||||||
|
g.inner.Y.SetZero()
|
||||||
|
return 64, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := g.inner.X.SetBytesCanonical(buf[:32]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := g.inner.Y.SetBytesCanonical(buf[32:64]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !g.inner.IsOnCurve() {
|
||||||
|
return 0, errors.New("point is not on curve")
|
||||||
|
}
|
||||||
|
if !g.inner.IsInSubGroup() {
|
||||||
|
return 0, errors.New("point is not in correct subgroup")
|
||||||
|
}
|
||||||
|
return 64, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal serializes the point into a byte slice.
|
// Marshal serializes the point into a byte slice.
|
||||||
//
|
//
|
||||||
// Note: The point is serialized as uncompressed.
|
// The output is in EVM format: 64 bytes total.
|
||||||
|
// [32-byte x coordinate][32-byte y coordinate]
|
||||||
|
// where each coordinate is a big-endian integer padded to 32 bytes.
|
||||||
func (p *G1) Marshal() []byte {
|
func (p *G1) Marshal() []byte {
|
||||||
return p.inner.Marshal()
|
output := make([]byte, 64)
|
||||||
|
|
||||||
|
xBytes := p.inner.X.Bytes()
|
||||||
|
copy(output[:32], xBytes[:])
|
||||||
|
|
||||||
|
yBytes := p.inner.Y.Bytes()
|
||||||
|
copy(output[32:64], yBytes[:])
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
func allZeroes(buf []byte) bool {
|
||||||
|
for i := range buf {
|
||||||
|
if buf[i] != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package bn256
|
package bn256
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bn254"
|
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -18,21 +20,66 @@ type G2 struct {
|
||||||
|
|
||||||
// Unmarshal deserializes `buf` into `g`
|
// Unmarshal deserializes `buf` into `g`
|
||||||
//
|
//
|
||||||
// Note: whether the deserialization is of a compressed
|
// The input is expected to be in the EVM format:
|
||||||
// or an uncompressed point, is encoded in the bytes.
|
// 128 bytes: [32-byte x.0][32-byte x.1][32-byte y.0][32-byte y.1]
|
||||||
//
|
// where each value is a big-endian integer.
|
||||||
// For our purpose, the point will always be serialized
|
|
||||||
// as uncompressed, ie 128 bytes.
|
|
||||||
//
|
//
|
||||||
// This method also checks whether the point is on the
|
// This method also checks whether the point is on the
|
||||||
// curve and in the prime order subgroup.
|
// curve and in the prime order subgroup.
|
||||||
func (g *G2) Unmarshal(buf []byte) (int, error) {
|
func (g *G2) Unmarshal(buf []byte) (int, error) {
|
||||||
return g.inner.SetBytes(buf)
|
if len(buf) < 128 {
|
||||||
|
return 0, errors.New("invalid G2 point size")
|
||||||
|
}
|
||||||
|
|
||||||
|
if allZeroes(buf[:128]) {
|
||||||
|
// point at infinity
|
||||||
|
g.inner.X.A0.SetZero()
|
||||||
|
g.inner.X.A1.SetZero()
|
||||||
|
g.inner.Y.A0.SetZero()
|
||||||
|
g.inner.Y.A1.SetZero()
|
||||||
|
return 128, nil
|
||||||
|
}
|
||||||
|
if err := g.inner.X.A0.SetBytesCanonical(buf[0:32]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := g.inner.X.A1.SetBytesCanonical(buf[32:64]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := g.inner.Y.A0.SetBytesCanonical(buf[64:96]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if err := g.inner.Y.A1.SetBytesCanonical(buf[96:128]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !g.inner.IsOnCurve() {
|
||||||
|
return 0, errors.New("point is not on curve")
|
||||||
|
}
|
||||||
|
if !g.inner.IsInSubGroup() {
|
||||||
|
return 0, errors.New("point is not in correct subgroup")
|
||||||
|
}
|
||||||
|
return 128, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal serializes the point into a byte slice.
|
// Marshal serializes the point into a byte slice.
|
||||||
//
|
//
|
||||||
// Note: The point is serialized as uncompressed.
|
// The output is in EVM format: 128 bytes total.
|
||||||
|
// [32-byte x.0][32-byte x.1][32-byte y.0][32-byte y.1]
|
||||||
|
// where each value is a big-endian integer.
|
||||||
func (g *G2) Marshal() []byte {
|
func (g *G2) Marshal() []byte {
|
||||||
return g.inner.Marshal()
|
output := make([]byte, 128)
|
||||||
|
|
||||||
|
xA0Bytes := g.inner.X.A0.Bytes()
|
||||||
|
copy(output[:32], xA0Bytes[:])
|
||||||
|
|
||||||
|
xA1Bytes := g.inner.X.A1.Bytes()
|
||||||
|
copy(output[32:64], xA1Bytes[:])
|
||||||
|
|
||||||
|
yA0Bytes := g.inner.Y.A0.Bytes()
|
||||||
|
copy(output[64:96], yA0Bytes[:])
|
||||||
|
|
||||||
|
yA1Bytes := g.inner.Y.A1.Bytes()
|
||||||
|
copy(output[96:128], yA1Bytes[:])
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
crypto/bn256/gnark/native_format_test.go
Normal file
42
crypto/bn256/gnark/native_format_test.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
package bn256
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/consensys/gnark-crypto/ecc/bn254"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNativeGnarkFormatIncompatibility(t *testing.T) {
|
||||||
|
// Use official gnark serialization
|
||||||
|
_, _, g1Gen, _ := bn254.Generators()
|
||||||
|
wrongSer := g1Gen.Bytes()
|
||||||
|
|
||||||
|
var evmG1 G1
|
||||||
|
_, err := evmG1.Unmarshal(wrongSer[:])
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("points serialized using the official bn254 serialization algorithm, should not work with the evm format")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerRoundTrip(t *testing.T) {
|
||||||
|
_, _, g1Gen, g2Gen := bn254.Generators()
|
||||||
|
|
||||||
|
expectedG1 := G1{inner: g1Gen}
|
||||||
|
bytesG1 := expectedG1.Marshal()
|
||||||
|
|
||||||
|
expectedG2 := G2{inner: g2Gen}
|
||||||
|
bytesG2 := expectedG2.Marshal()
|
||||||
|
|
||||||
|
var gotG1 G1
|
||||||
|
gotG1.Unmarshal(bytesG1)
|
||||||
|
|
||||||
|
var gotG2 G2
|
||||||
|
gotG2.Unmarshal(bytesG2)
|
||||||
|
|
||||||
|
if !expectedG1.inner.Equal(&gotG1.inner) {
|
||||||
|
t.Errorf("serialization roundtrip failed for G1")
|
||||||
|
}
|
||||||
|
if !expectedG2.inner.Equal(&gotG2.inner) {
|
||||||
|
t.Errorf("serialization roundtrip failed for G2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -128,7 +128,7 @@ func (e *G1) Marshal() []byte {
|
||||||
func (e *G1) Unmarshal(m []byte) ([]byte, error) {
|
func (e *G1) Unmarshal(m []byte) ([]byte, error) {
|
||||||
// Each value is a 256-bit number.
|
// Each value is a 256-bit number.
|
||||||
const numBytes = 256 / 8
|
const numBytes = 256 / 8
|
||||||
if len(m) != 2*numBytes {
|
if len(m) < 2*numBytes {
|
||||||
return nil, errors.New("bn256: not enough data")
|
return nil, errors.New("bn256: not enough data")
|
||||||
}
|
}
|
||||||
// Unmarshal the points and check their caps
|
// Unmarshal the points and check their caps
|
||||||
|
|
@ -253,7 +253,7 @@ func (n *G2) Marshal() []byte {
|
||||||
func (e *G2) Unmarshal(m []byte) ([]byte, error) {
|
func (e *G2) Unmarshal(m []byte) ([]byte, error) {
|
||||||
// Each value is a 256-bit number.
|
// Each value is a 256-bit number.
|
||||||
const numBytes = 256 / 8
|
const numBytes = 256 / 8
|
||||||
if len(m) != 4*numBytes {
|
if len(m) < 4*numBytes {
|
||||||
return nil, errors.New("bn256: not enough data")
|
return nil, errors.New("bn256: not enough data")
|
||||||
}
|
}
|
||||||
// Unmarshal the points and check their caps
|
// Unmarshal the points and check their caps
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -297,3 +298,38 @@ func TestPythonIntegration(t *testing.T) {
|
||||||
t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
|
t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
|
||||||
t.Logf("msg: %x, privkey: %s sig: %x\n", msg1, kh, sig1)
|
t.Logf("msg: %x, privkey: %s sig: %x\n", msg1, kh, sig1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// goos: darwin
|
||||||
|
// goarch: arm64
|
||||||
|
// pkg: github.com/ethereum/go-ethereum/crypto
|
||||||
|
// cpu: Apple M1 Pro
|
||||||
|
// BenchmarkKeccak256Hash
|
||||||
|
// BenchmarkKeccak256Hash-8 931095 1270 ns/op 32 B/op 1 allocs/op
|
||||||
|
func BenchmarkKeccak256Hash(b *testing.B) {
|
||||||
|
var input [512]byte
|
||||||
|
rand.Read(input[:])
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Keccak256Hash(input[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// goos: darwin
|
||||||
|
// goarch: arm64
|
||||||
|
// pkg: github.com/ethereum/go-ethereum/crypto
|
||||||
|
// cpu: Apple M1 Pro
|
||||||
|
// BenchmarkHashData
|
||||||
|
// BenchmarkHashData-8 793386 1278 ns/op 32 B/op 1 allocs/op
|
||||||
|
func BenchmarkHashData(b *testing.B) {
|
||||||
|
var (
|
||||||
|
input [512]byte
|
||||||
|
buffer = NewKeccakState()
|
||||||
|
)
|
||||||
|
rand.Read(input[:])
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
HashData(buffer, input[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ var (
|
||||||
blobT = reflect.TypeOf(Blob{})
|
blobT = reflect.TypeOf(Blob{})
|
||||||
commitmentT = reflect.TypeOf(Commitment{})
|
commitmentT = reflect.TypeOf(Commitment{})
|
||||||
proofT = reflect.TypeOf(Proof{})
|
proofT = reflect.TypeOf(Proof{})
|
||||||
|
|
||||||
|
CellProofsPerBlob = 128
|
||||||
)
|
)
|
||||||
|
|
||||||
// Blob represents a 4844 data blob.
|
// Blob represents a 4844 data blob.
|
||||||
|
|
@ -45,7 +47,7 @@ func (b *Blob) UnmarshalJSON(input []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalText returns the hex representation of b.
|
// MarshalText returns the hex representation of b.
|
||||||
func (b Blob) MarshalText() ([]byte, error) {
|
func (b *Blob) MarshalText() ([]byte, error) {
|
||||||
return hexutil.Bytes(b[:]).MarshalText()
|
return hexutil.Bytes(b[:]).MarshalText()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,6 +151,16 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
return gokzgVerifyBlobProof(blob, commitment, proof)
|
return gokzgVerifyBlobProof(blob, commitment, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyCellProofs verifies a batch of proofs corresponding to the blobs and commitments.
|
||||||
|
// Expects length of blobs and commitments to be equal.
|
||||||
|
// Expects length of proofs be 128 * length of blobs.
|
||||||
|
func VerifyCellProofs(blobs []Blob, commitments []Commitment, proofs []Proof) error {
|
||||||
|
if useCKZG.Load() {
|
||||||
|
return ckzgVerifyCellProofBatch(blobs, commitments, proofs)
|
||||||
|
}
|
||||||
|
return gokzgVerifyCellProofBatch(blobs, commitments, proofs)
|
||||||
|
}
|
||||||
|
|
||||||
// ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
// ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
// the commitment.
|
// the commitment.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -149,3 +149,44 @@ func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
}
|
}
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ckzgVerifyCellProofs verifies that the blob data corresponds to the provided commitment.
|
||||||
|
func ckzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
||||||
|
ckzgIniter.Do(ckzgInit)
|
||||||
|
var (
|
||||||
|
proofs = make([]ckzg4844.Bytes48, len(cellProofs))
|
||||||
|
commits = make([]ckzg4844.Bytes48, 0, len(cellProofs))
|
||||||
|
cellIndices = make([]uint64, 0, len(cellProofs))
|
||||||
|
cells = make([]ckzg4844.Cell, 0, len(cellProofs))
|
||||||
|
)
|
||||||
|
// Copy over the cell proofs
|
||||||
|
for i, proof := range cellProofs {
|
||||||
|
proofs[i] = (ckzg4844.Bytes48)(proof)
|
||||||
|
}
|
||||||
|
// Blow up the commitments to be the same length as the proofs
|
||||||
|
for _, commitment := range commitments {
|
||||||
|
for range gokzg4844.CellsPerExtBlob {
|
||||||
|
commits = append(commits, (ckzg4844.Bytes48)(commitment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Compute the cells and cell indices
|
||||||
|
for i := range blobs {
|
||||||
|
cellsI, err := ckzg4844.ComputeCells((*ckzg4844.Blob)(&blobs[i]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cells = append(cells, cellsI[:]...)
|
||||||
|
for idx := range len(cellsI) {
|
||||||
|
cellIndices = append(cellIndices, uint64(idx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valid, err := ckzg4844.VerifyCellKZGProofBatch(commits, cellIndices, cells, proofs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
return errors.New("invalid proof")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,11 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
panic("unsupported platform")
|
panic("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ckzgVerifyCellProofBatch verifies that the blob data corresponds to the provided commitment.
|
||||||
|
func ckzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, proof []Proof) error {
|
||||||
|
panic("unsupported platform")
|
||||||
|
}
|
||||||
|
|
||||||
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
// the commitment.
|
// the commitment.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -114,3 +114,37 @@ func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
}
|
}
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gokzgVerifyCellProofs verifies that the blob data corresponds to the provided commitment.
|
||||||
|
func gokzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
||||||
|
gokzgIniter.Do(gokzgInit)
|
||||||
|
|
||||||
|
var (
|
||||||
|
proofs = make([]gokzg4844.KZGProof, len(cellProofs))
|
||||||
|
commits = make([]gokzg4844.KZGCommitment, 0, len(cellProofs))
|
||||||
|
cellIndices = make([]uint64, 0, len(cellProofs))
|
||||||
|
cells = make([]*gokzg4844.Cell, 0, len(cellProofs))
|
||||||
|
)
|
||||||
|
// Copy over the cell proofs
|
||||||
|
for i, proof := range cellProofs {
|
||||||
|
proofs[i] = gokzg4844.KZGProof(proof)
|
||||||
|
}
|
||||||
|
// Blow up the commitments to be the same length as the proofs
|
||||||
|
for _, commitment := range commitments {
|
||||||
|
for range gokzg4844.CellsPerExtBlob {
|
||||||
|
commits = append(commits, gokzg4844.KZGCommitment(commitment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Compute the cell and cell indices
|
||||||
|
for i := range blobs {
|
||||||
|
cellsI, err := context.ComputeCells((*gokzg4844.Blob)(&blobs[i]), 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cells = append(cells, cellsI[:]...)
|
||||||
|
for idx := range len(cellsI) {
|
||||||
|
cellIndices = append(cellIndices, uint64(idx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context.VerifyCellKZGProofBatch(commits, cellIndices, cells[:], proofs)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -193,3 +193,40 @@ func benchmarkVerifyBlobProof(b *testing.B, ckzg bool) {
|
||||||
VerifyBlobProof(blob, commitment, proof)
|
VerifyBlobProof(blob, commitment, proof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCKZGCells(t *testing.T) { testKZGCells(t, true) }
|
||||||
|
func TestGoKZGCells(t *testing.T) { testKZGCells(t, false) }
|
||||||
|
func testKZGCells(t *testing.T, ckzg bool) {
|
||||||
|
if ckzg && !ckzgAvailable {
|
||||||
|
t.Skip("CKZG unavailable in this test build")
|
||||||
|
}
|
||||||
|
defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
|
||||||
|
useCKZG.Store(ckzg)
|
||||||
|
|
||||||
|
blob1 := randBlob()
|
||||||
|
blob2 := randBlob()
|
||||||
|
|
||||||
|
commitment1, err := BlobToCommitment(blob1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create KZG commitment from blob: %v", err)
|
||||||
|
}
|
||||||
|
commitment2, err := BlobToCommitment(blob2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create KZG commitment from blob: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proofs1, err := ComputeCellProofs(blob1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create KZG proof at point: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proofs2, err := ComputeCellProofs(blob2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create KZG proof at point: %v", err)
|
||||||
|
}
|
||||||
|
proofs := append(proofs1, proofs2...)
|
||||||
|
blobs := []Blob{*blob1, *blob2}
|
||||||
|
if err := VerifyCellProofs(blobs, []Commitment{commitment1, commitment2}, proofs); err != nil {
|
||||||
|
t.Fatalf("failed to verify KZG proof at point: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -62,6 +63,26 @@ import (
|
||||||
gethversion "github.com/ethereum/go-ethereum/version"
|
gethversion "github.com/ethereum/go-ethereum/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// This is the fairness knob for the discovery mixer. When looking for peers, we'll
|
||||||
|
// wait this long for a single source of candidates before moving on and trying other
|
||||||
|
// sources. If this timeout expires, the source will be skipped in this round, but it
|
||||||
|
// will continue to fetch in the background and will have a chance with a new timeout
|
||||||
|
// in the next rounds, giving it overall more time but a proportionally smaller share.
|
||||||
|
// We expect a normal source to produce ~10 candidates per second.
|
||||||
|
discmixTimeout = 100 * time.Millisecond
|
||||||
|
|
||||||
|
// discoveryPrefetchBuffer is the number of peers to pre-fetch from a discovery
|
||||||
|
// source. It is useful to avoid the negative effects of potential longer timeouts
|
||||||
|
// in the discovery, keeping dial progress while waiting for the next batch of
|
||||||
|
// candidates.
|
||||||
|
discoveryPrefetchBuffer = 32
|
||||||
|
|
||||||
|
// maxParallelENRRequests is the maximum number of parallel ENR requests that can be
|
||||||
|
// performed by a disc/v4 source.
|
||||||
|
maxParallelENRRequests = 16
|
||||||
|
)
|
||||||
|
|
||||||
// Config contains the configuration options of the ETH protocol.
|
// Config contains the configuration options of the ETH protocol.
|
||||||
// Deprecated: use ethconfig.Config instead.
|
// Deprecated: use ethconfig.Config instead.
|
||||||
type Config = ethconfig.Config
|
type Config = ethconfig.Config
|
||||||
|
|
@ -71,6 +92,7 @@ type Ethereum struct {
|
||||||
// core protocol objects
|
// core protocol objects
|
||||||
config *ethconfig.Config
|
config *ethconfig.Config
|
||||||
txPool *txpool.TxPool
|
txPool *txpool.TxPool
|
||||||
|
blobTxPool *blobpool.BlobPool
|
||||||
localTxTracker *locals.TxTracker
|
localTxTracker *locals.TxTracker
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
|
|
||||||
|
|
@ -176,7 +198,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
networkID: networkID,
|
networkID: networkID,
|
||||||
gasPrice: config.Miner.GasPrice,
|
gasPrice: config.Miner.GasPrice,
|
||||||
p2pServer: stack.Server(),
|
p2pServer: stack.Server(),
|
||||||
discmix: enode.NewFairMix(0),
|
discmix: enode.NewFairMix(discmixTimeout),
|
||||||
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
|
shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
|
||||||
}
|
}
|
||||||
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
||||||
|
|
@ -263,9 +285,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
if config.BlobPool.Datadir != "" {
|
if config.BlobPool.Datadir != "" {
|
||||||
config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir)
|
config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir)
|
||||||
}
|
}
|
||||||
blobPool := blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth)
|
eth.blobTxPool = blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth)
|
||||||
|
|
||||||
eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, blobPool})
|
eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, eth.blobTxPool})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +396,7 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
||||||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||||
func (s *Ethereum) TxPool() *txpool.TxPool { return s.txPool }
|
func (s *Ethereum) TxPool() *txpool.TxPool { return s.txPool }
|
||||||
|
func (s *Ethereum) BlobTxPool() *blobpool.BlobPool { return s.blobTxPool }
|
||||||
func (s *Ethereum) Engine() consensus.Engine { return s.engine }
|
func (s *Ethereum) Engine() consensus.Engine { return s.engine }
|
||||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||||
|
|
@ -494,10 +517,27 @@ func (s *Ethereum) setupDiscovery() error {
|
||||||
s.discmix.AddSource(iter)
|
s.discmix.AddSource(iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add DHT nodes from discv4.
|
||||||
|
if s.p2pServer.DiscoveryV4() != nil {
|
||||||
|
iter := s.p2pServer.DiscoveryV4().RandomNodes()
|
||||||
|
resolverFunc := func(ctx context.Context, enr *enode.Node) *enode.Node {
|
||||||
|
// RequestENR does not yet support context. It will simply time out.
|
||||||
|
// If the ENR can't be resolved, RequestENR will return nil. We don't
|
||||||
|
// care about the specific error here, so we ignore it.
|
||||||
|
nn, _ := s.p2pServer.DiscoveryV4().RequestENR(enr)
|
||||||
|
return nn
|
||||||
|
}
|
||||||
|
iter = enode.AsyncFilter(iter, resolverFunc, maxParallelENRRequests)
|
||||||
|
iter = enode.Filter(iter, eth.NewNodeFilter(s.blockchain))
|
||||||
|
iter = enode.NewBufferIter(iter, discoveryPrefetchBuffer)
|
||||||
|
s.discmix.AddSource(iter)
|
||||||
|
}
|
||||||
|
|
||||||
// Add DHT nodes from discv5.
|
// Add DHT nodes from discv5.
|
||||||
if s.p2pServer.DiscoveryV5() != nil {
|
if s.p2pServer.DiscoveryV5() != nil {
|
||||||
filter := eth.NewNodeFilter(s.blockchain)
|
filter := eth.NewNodeFilter(s.blockchain)
|
||||||
iter := enode.Filter(s.p2pServer.DiscoveryV5().RandomNodes(), filter)
|
iter := enode.Filter(s.p2pServer.DiscoveryV5().RandomNodes(), filter)
|
||||||
|
iter = enode.NewBufferIter(iter, discoveryPrefetchBuffer)
|
||||||
s.discmix.AddSource(iter)
|
s.discmix.AddSource(iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,12 @@
|
||||||
package catalyst
|
package catalyst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||||
|
|
@ -29,10 +31,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"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/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/internal/version"
|
"github.com/ethereum/go-ethereum/internal/version"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -91,7 +95,9 @@ var caps = []string{
|
||||||
"engine_getPayloadV2",
|
"engine_getPayloadV2",
|
||||||
"engine_getPayloadV3",
|
"engine_getPayloadV3",
|
||||||
"engine_getPayloadV4",
|
"engine_getPayloadV4",
|
||||||
|
"engine_getPayloadV5",
|
||||||
"engine_getBlobsV1",
|
"engine_getBlobsV1",
|
||||||
|
"engine_getBlobsV2",
|
||||||
"engine_newPayloadV1",
|
"engine_newPayloadV1",
|
||||||
"engine_newPayloadV2",
|
"engine_newPayloadV2",
|
||||||
"engine_newPayloadV3",
|
"engine_newPayloadV3",
|
||||||
|
|
@ -111,6 +117,17 @@ var caps = []string{
|
||||||
"engine_getClientVersionV1",
|
"engine_getClientVersionV1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Number of blobs requested via getBlobsV2
|
||||||
|
getBlobsRequestedCounter = metrics.NewRegisteredCounter("engine/getblobs/requested", nil)
|
||||||
|
// Number of blobs requested via getBlobsV2 that are present in the blobpool
|
||||||
|
getBlobsAvailableCounter = metrics.NewRegisteredCounter("engine/getblobs/available", nil)
|
||||||
|
// Number of times getBlobsV2 responded with “hit”
|
||||||
|
getBlobsV2RequestHit = metrics.NewRegisteredCounter("engine/getblobs/hit", nil)
|
||||||
|
// Number of times getBlobsV2 responded with “miss”
|
||||||
|
getBlobsV2RequestMiss = metrics.NewRegisteredCounter("engine/getblobs/miss", nil)
|
||||||
|
)
|
||||||
|
|
||||||
type ConsensusAPI struct {
|
type ConsensusAPI struct {
|
||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
|
|
||||||
|
|
@ -143,12 +160,9 @@ type ConsensusAPI struct {
|
||||||
// Geth can appear to be stuck or do strange things if the beacon client is
|
// Geth can appear to be stuck or do strange things if the beacon client is
|
||||||
// offline or is sending us strange data. Stash some update stats away so
|
// offline or is sending us strange data. Stash some update stats away so
|
||||||
// that we can warn the user and not have them open issues on our tracker.
|
// that we can warn the user and not have them open issues on our tracker.
|
||||||
lastTransitionUpdate time.Time
|
lastTransitionUpdate atomic.Int64
|
||||||
lastTransitionLock sync.Mutex
|
lastForkchoiceUpdate atomic.Int64
|
||||||
lastForkchoiceUpdate time.Time
|
lastNewPayloadUpdate atomic.Int64
|
||||||
lastForkchoiceLock sync.Mutex
|
|
||||||
lastNewPayloadUpdate time.Time
|
|
||||||
lastNewPayloadLock sync.Mutex
|
|
||||||
|
|
||||||
forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method
|
forkchoiceLock sync.Mutex // Lock for the forkChoiceUpdated method
|
||||||
newPayloadLock sync.Mutex // Lock for the NewPayload method
|
newPayloadLock sync.Mutex // Lock for the NewPayload method
|
||||||
|
|
@ -231,7 +245,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa
|
||||||
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
case params.BeaconRoot == nil:
|
case params.BeaconRoot == nil:
|
||||||
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||||
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague, forks.Osaka):
|
||||||
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,9 +266,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
return engine.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
|
return engine.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
|
||||||
}
|
}
|
||||||
// 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
|
||||||
api.lastForkchoiceLock.Lock()
|
api.lastForkchoiceUpdate.Store(time.Now().Unix())
|
||||||
api.lastForkchoiceUpdate = time.Now()
|
|
||||||
api.lastForkchoiceLock.Unlock()
|
|
||||||
|
|
||||||
// Check whether we have the block yet in our database or not. If not, we'll
|
// Check whether we have the block yet in our database or not. If not, we'll
|
||||||
// need to either trigger a sync, or to reject this forkchoice update for a
|
// need to either trigger a sync, or to reject this forkchoice update for a
|
||||||
|
|
@ -271,9 +283,15 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
// that should be fixed, not papered over.
|
// that should be fixed, not papered over.
|
||||||
header := api.remoteBlocks.get(update.HeadBlockHash)
|
header := api.remoteBlocks.get(update.HeadBlockHash)
|
||||||
if header == nil {
|
if header == nil {
|
||||||
log.Warn("Forkchoice requested unknown head", "hash", update.HeadBlockHash)
|
log.Warn("Fetching the unknown forkchoice head from network", "hash", update.HeadBlockHash)
|
||||||
|
retrievedHead, err := api.eth.Downloader().GetHeader(update.HeadBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Could not retrieve unknown head from peers")
|
||||||
return engine.STATUS_SYNCING, nil
|
return engine.STATUS_SYNCING, nil
|
||||||
}
|
}
|
||||||
|
api.remoteBlocks.put(retrievedHead.Hash(), retrievedHead)
|
||||||
|
header = retrievedHead
|
||||||
|
}
|
||||||
// If the finalized hash is known, we can direct the downloader to move
|
// If the finalized hash is known, we can direct the downloader to move
|
||||||
// potentially more data to the freezer from the get go.
|
// potentially more data to the freezer from the get go.
|
||||||
finalized := api.remoteBlocks.get(update.FinalizedBlockHash)
|
finalized := api.remoteBlocks.get(update.FinalizedBlockHash)
|
||||||
|
|
@ -398,9 +416,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
|
||||||
return nil, errors.New("invalid terminal total difficulty")
|
return nil, errors.New("invalid terminal total difficulty")
|
||||||
}
|
}
|
||||||
// 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
|
||||||
api.lastTransitionLock.Lock()
|
api.lastTransitionUpdate.Store(time.Now().Unix())
|
||||||
api.lastTransitionUpdate = time.Now()
|
|
||||||
api.lastTransitionLock.Unlock()
|
|
||||||
|
|
||||||
ttd := api.config().TerminalTotalDifficulty
|
ttd := api.config().TerminalTotalDifficulty
|
||||||
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
||||||
|
|
@ -456,6 +472,14 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu
|
||||||
return api.getPayload(payloadID, false)
|
return api.getPayload(payloadID, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPayloadV5 returns a cached payload by id.
|
||||||
|
func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||||
|
if !payloadID.Is(engine.PayloadV3) {
|
||||||
|
return nil, engine.UnsupportedFork
|
||||||
|
}
|
||||||
|
return api.getPayload(payloadID, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) {
|
func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) {
|
||||||
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
||||||
data := api.localBlocks.get(payloadID, full)
|
data := api.localBlocks.get(payloadID, full)
|
||||||
|
|
@ -470,14 +494,87 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
|
||||||
if len(hashes) > 128 {
|
if len(hashes) > 128 {
|
||||||
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
||||||
}
|
}
|
||||||
res := make([]*engine.BlobAndProofV1, len(hashes))
|
var (
|
||||||
|
res = make([]*engine.BlobAndProofV1, len(hashes))
|
||||||
|
hasher = sha256.New()
|
||||||
|
index = make(map[common.Hash]int)
|
||||||
|
sidecars = api.eth.BlobTxPool().GetBlobs(hashes)
|
||||||
|
)
|
||||||
|
|
||||||
blobs, proofs := api.eth.TxPool().GetBlobs(hashes)
|
for i, hash := range hashes {
|
||||||
for i := 0; i < len(blobs); i++ {
|
index[hash] = i
|
||||||
if blobs[i] != nil {
|
}
|
||||||
res[i] = &engine.BlobAndProofV1{
|
for i, sidecar := range sidecars {
|
||||||
Blob: (*blobs[i])[:],
|
if res[i] != nil || sidecar == nil {
|
||||||
Proof: (*proofs[i])[:],
|
// already filled
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for cIdx, commitment := range sidecar.Commitments {
|
||||||
|
computed := kzg4844.CalcBlobHashV1(hasher, &commitment)
|
||||||
|
if idx, ok := index[computed]; ok {
|
||||||
|
res[idx] = &engine.BlobAndProofV1{
|
||||||
|
Blob: sidecar.Blobs[cIdx][:],
|
||||||
|
Proof: sidecar.Proofs[cIdx][:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlobsV2 returns a blob from the transaction pool.
|
||||||
|
func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProofV2, error) {
|
||||||
|
if len(hashes) > 128 {
|
||||||
|
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
||||||
|
}
|
||||||
|
|
||||||
|
available := api.eth.BlobTxPool().AvailableBlobs(hashes)
|
||||||
|
getBlobsRequestedCounter.Inc(int64(len(hashes)))
|
||||||
|
getBlobsAvailableCounter.Inc(int64(available))
|
||||||
|
// Optimization: check first if all blobs are available, if not, return empty response
|
||||||
|
if available != len(hashes) {
|
||||||
|
getBlobsV2RequestMiss.Inc(1)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
getBlobsV2RequestHit.Inc(1)
|
||||||
|
|
||||||
|
// pull up the blob hashes
|
||||||
|
var (
|
||||||
|
res = make([]*engine.BlobAndProofV2, len(hashes))
|
||||||
|
index = make(map[common.Hash][]int)
|
||||||
|
sidecars = api.eth.BlobTxPool().GetBlobs(hashes)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, hash := range hashes {
|
||||||
|
index[hash] = append(index[hash], i)
|
||||||
|
}
|
||||||
|
for i, sidecar := range sidecars {
|
||||||
|
if res[i] != nil {
|
||||||
|
// already filled
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if sidecar == nil {
|
||||||
|
// not found, return empty response
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if sidecar.Version != 1 {
|
||||||
|
log.Info("GetBlobs queried V0 transaction: index %v, blobhashes %v", index, sidecar.BlobHashes())
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
blobHashes := sidecar.BlobHashes()
|
||||||
|
for bIdx, hash := range blobHashes {
|
||||||
|
if idxes, ok := index[hash]; ok {
|
||||||
|
proofs := sidecar.CellProofsAt(bIdx)
|
||||||
|
var cellProofs []hexutil.Bytes
|
||||||
|
for _, proof := range proofs {
|
||||||
|
cellProofs = append(cellProofs, proof[:])
|
||||||
|
}
|
||||||
|
for _, idx := range idxes {
|
||||||
|
res[idx] = &engine.BlobAndProofV2{
|
||||||
|
Blob: sidecar.Blobs[bIdx][:],
|
||||||
|
CellProofs: cellProofs,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -550,7 +647,7 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas
|
||||||
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
case executionRequests == nil:
|
case executionRequests == nil:
|
||||||
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||||
case !api.checkFork(params.Timestamp, forks.Prague):
|
case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka):
|
||||||
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
|
|
@ -611,9 +708,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
||||||
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
|
||||||
api.lastNewPayloadLock.Lock()
|
api.lastNewPayloadUpdate.Store(time.Now().Unix())
|
||||||
api.lastNewPayloadUpdate = time.Now()
|
|
||||||
api.lastNewPayloadLock.Unlock()
|
|
||||||
|
|
||||||
// If we already have the block locally, ignore the entire execution and just
|
// If we already have the block locally, ignore the entire execution and just
|
||||||
// return a fake success.
|
// return a fake success.
|
||||||
|
|
@ -814,17 +909,9 @@ func (api *ConsensusAPI) heartbeat() {
|
||||||
// Sleep a bit and retrieve the last known consensus updates
|
// Sleep a bit and retrieve the last known consensus updates
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
api.lastTransitionLock.Lock()
|
lastTransitionUpdate := time.Unix(api.lastTransitionUpdate.Load(), 0)
|
||||||
lastTransitionUpdate := api.lastTransitionUpdate
|
lastForkchoiceUpdate := time.Unix(api.lastForkchoiceUpdate.Load(), 0)
|
||||||
api.lastTransitionLock.Unlock()
|
lastNewPayloadUpdate := time.Unix(api.lastNewPayloadUpdate.Load(), 0)
|
||||||
|
|
||||||
api.lastForkchoiceLock.Lock()
|
|
||||||
lastForkchoiceUpdate := api.lastForkchoiceUpdate
|
|
||||||
api.lastForkchoiceLock.Unlock()
|
|
||||||
|
|
||||||
api.lastNewPayloadLock.Lock()
|
|
||||||
lastNewPayloadUpdate := api.lastNewPayloadUpdate
|
|
||||||
api.lastNewPayloadLock.Unlock()
|
|
||||||
|
|
||||||
// If there have been no updates for the past while, warn the user
|
// If there have been no updates for the past while, warn the user
|
||||||
// that the beacon client is probably offline
|
// that the beacon client is probably offline
|
||||||
|
|
|
||||||
|
|
@ -280,9 +280,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, 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
|
||||||
api.lastNewPayloadLock.Lock()
|
api.lastNewPayloadUpdate.Store(time.Now().Unix())
|
||||||
api.lastNewPayloadUpdate = time.Now()
|
|
||||||
api.lastNewPayloadLock.Unlock()
|
|
||||||
|
|
||||||
log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash)
|
log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash)
|
||||||
stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness)
|
stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -48,34 +49,39 @@ func (d *Downloader) BeaconDevSync(mode SyncMode, hash common.Hash, stop chan st
|
||||||
return errors.New("stop requested")
|
return errors.New("stop requested")
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
// Pick a random peer to sync from and keep retrying if none are yet
|
header, err := d.GetHeader(hash)
|
||||||
// available due to fresh startup
|
if err != nil {
|
||||||
d.peers.lock.RLock()
|
|
||||||
var peer *peerConnection
|
|
||||||
for _, peer = range d.peers.peers {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
d.peers.lock.RUnlock()
|
|
||||||
|
|
||||||
if peer == nil {
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return d.BeaconSync(mode, header, header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeader tries to retrieve the header with a given hash from a random peer.
|
||||||
|
func (d *Downloader) GetHeader(hash common.Hash) (*types.Header, error) {
|
||||||
|
// Pick a random peer to sync from and keep retrying if none are yet
|
||||||
|
// available due to fresh startup
|
||||||
|
d.peers.lock.RLock()
|
||||||
|
defer d.peers.lock.RUnlock()
|
||||||
|
|
||||||
|
for _, peer := range d.peers.peers {
|
||||||
|
if peer == nil {
|
||||||
|
return nil, errors.New("could not find peer")
|
||||||
|
}
|
||||||
// Found a peer, attempt to retrieve the header whilst blocking and
|
// Found a peer, attempt to retrieve the header whilst blocking and
|
||||||
// retry if it fails for whatever reason
|
// retry if it fails for whatever reason
|
||||||
log.Info("Attempting to retrieve sync target", "peer", peer.id)
|
log.Debug("Attempting to retrieve sync target", "peer", peer.id, "hash", hash)
|
||||||
headers, metas, err := d.fetchHeadersByHash(peer, hash, 1, 0, false)
|
headers, metas, err := d.fetchHeadersByHash(peer, hash, 1, 0, false)
|
||||||
if err != nil || len(headers) != 1 {
|
if err != nil || len(headers) != 1 {
|
||||||
log.Warn("Failed to fetch sync target", "headers", len(headers), "err", err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Head header retrieved, if the hash matches, start the actual sync
|
// Head header retrieved, if the hash matches, start the actual sync
|
||||||
if metas[0] != hash {
|
if metas[0] != hash {
|
||||||
log.Error("Received invalid sync target", "want", hash, "have", metas[0])
|
log.Warn("Received invalid sync target", "peer", peer.id, "want", hash, "have", metas[0])
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return d.BeaconSync(mode, headers[0], headers[0])
|
return headers[0], nil
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("failed to fetch sync target")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,9 @@ type BlockChain interface {
|
||||||
// InsertChain inserts a batch of blocks into the local chain.
|
// InsertChain inserts a batch of blocks into the local chain.
|
||||||
InsertChain(types.Blocks) (int, error)
|
InsertChain(types.Blocks) (int, error)
|
||||||
|
|
||||||
|
// StopInsert interrupts the inserting process.
|
||||||
|
StopInsert()
|
||||||
|
|
||||||
// InsertReceiptChain inserts a batch of blocks along with their receipts
|
// InsertReceiptChain inserts a batch of blocks along with their receipts
|
||||||
// into the local chain. Blocks older than the specified `ancientLimit`
|
// into the local chain. Blocks older than the specified `ancientLimit`
|
||||||
// are stored directly in the ancient store, while newer blocks are stored
|
// are stored directly in the ancient store, while newer blocks are stored
|
||||||
|
|
@ -634,6 +637,9 @@ func (d *Downloader) Cancel() {
|
||||||
// Terminate interrupts the downloader, canceling all pending operations.
|
// Terminate interrupts the downloader, canceling all pending operations.
|
||||||
// The downloader cannot be reused after calling Terminate.
|
// The downloader cannot be reused after calling Terminate.
|
||||||
func (d *Downloader) Terminate() {
|
func (d *Downloader) Terminate() {
|
||||||
|
// Signal to stop inserting in-flight blocks
|
||||||
|
d.blockchain.StopInsert()
|
||||||
|
|
||||||
// Close the termination channel (make sure double close is allowed)
|
// Close the termination channel (make sure double close is allowed)
|
||||||
d.quitLock.Lock()
|
d.quitLock.Lock()
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -83,9 +83,14 @@ func newFetchResult(header *types.Header, snapSync bool) *fetchResult {
|
||||||
} else if header.WithdrawalsHash != nil {
|
} else if header.WithdrawalsHash != nil {
|
||||||
item.Withdrawals = make(types.Withdrawals, 0)
|
item.Withdrawals = make(types.Withdrawals, 0)
|
||||||
}
|
}
|
||||||
if snapSync && !header.EmptyReceipts() {
|
if snapSync {
|
||||||
|
if header.EmptyReceipts() {
|
||||||
|
// Ensure the receipts list is valid even if it isn't actively fetched.
|
||||||
|
item.Receipts = rlp.EmptyList
|
||||||
|
} else {
|
||||||
item.pending.Store(item.pending.Load() | (1 << receiptType))
|
item.pending.Store(item.pending.Load() | (1 << receiptType))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type
|
||||||
if begin > 0 && end > 0 && begin > end {
|
if begin > 0 && end > 0 && begin > end {
|
||||||
return nil, errInvalidBlockRange
|
return nil, errInvalidBlockRange
|
||||||
}
|
}
|
||||||
if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) {
|
if begin >= 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) {
|
||||||
return nil, &history.PrunedHistoryError{}
|
return nil, &history.PrunedHistoryError{}
|
||||||
}
|
}
|
||||||
// Construct the range filter
|
// Construct the range filter
|
||||||
|
|
|
||||||
|
|
@ -463,7 +463,7 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*typ
|
||||||
// such as tx index, block hash, etc.
|
// such as tx index, block hash, etc.
|
||||||
// Notably tx hash is NOT filled in because it needs
|
// Notably tx hash is NOT filled in because it needs
|
||||||
// access to block body data.
|
// access to block body data.
|
||||||
cached, err := f.sys.cachedLogElem(ctx, hash, header.Number.Uint64())
|
cached, err := f.sys.cachedLogElem(ctx, hash, header.Number.Uint64(), header.Time)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ type logCacheElem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// cachedLogElem loads block logs from the backend and caches the result.
|
// cachedLogElem loads block logs from the backend and caches the result.
|
||||||
func (sys *FilterSystem) cachedLogElem(ctx context.Context, blockHash common.Hash, number uint64) (*logCacheElem, error) {
|
func (sys *FilterSystem) cachedLogElem(ctx context.Context, blockHash common.Hash, number, time uint64) (*logCacheElem, error) {
|
||||||
cached, ok := sys.logsCache.Get(blockHash)
|
cached, ok := sys.logsCache.Get(blockHash)
|
||||||
if ok {
|
if ok {
|
||||||
return cached, nil
|
return cached, nil
|
||||||
|
|
@ -119,6 +119,7 @@ func (sys *FilterSystem) cachedLogElem(ctx context.Context, blockHash common.Has
|
||||||
for _, log := range txLogs {
|
for _, log := range txLogs {
|
||||||
log.BlockHash = blockHash
|
log.BlockHash = blockHash
|
||||||
log.BlockNumber = number
|
log.BlockNumber = number
|
||||||
|
log.BlockTimestamp = time
|
||||||
log.TxIndex = uint(i)
|
log.TxIndex = uint(i)
|
||||||
log.Index = logIdx
|
log.Index = logIdx
|
||||||
logIdx++
|
logIdx++
|
||||||
|
|
|
||||||
|
|
@ -317,26 +317,26 @@ func testFilters(t *testing.T, history uint64, noHistory bool) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
f: sys.NewBlockFilter(chain[2].Hash(), []common.Address{contract}, nil),
|
f: sys.NewBlockFilter(chain[2].Hash(), []common.Address{contract}, nil),
|
||||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","blockTimestamp":30,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{contract}, [][]common.Hash{{hash1, hash2, hash3, hash4}}),
|
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{contract}, [][]common.Hash{{hash1, hash2, hash3, hash4}}),
|
||||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","blockTimestamp":20,"logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","blockTimestamp":30,"logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","blockTimestamp":10000,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(900, 999, []common.Address{contract}, [][]common.Hash{{hash3}}),
|
f: sys.NewRangeFilter(900, 999, []common.Address{contract}, [][]common.Hash{{hash3}}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{contract2}, [][]common.Hash{{hash3}}),
|
f: sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{contract2}, [][]common.Hash{{hash3}}),
|
||||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","blockTimestamp":9990,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(1, 10, []common.Address{contract}, [][]common.Hash{{hash2}, {hash1}}),
|
f: sys.NewRangeFilter(1, 10, []common.Address{contract}, [][]common.Hash{{hash2}, {hash1}}),
|
||||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","blockTimestamp":30,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
|
f: sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
|
||||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","blockTimestamp":20,"logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","blockTimestamp":20,"logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","blockTimestamp":30,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
|
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
|
||||||
|
|
@ -349,15 +349,15 @@ func testFilters(t *testing.T, history uint64, noHistory bool) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
||||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","blockTimestamp":10000,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
||||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","blockTimestamp":9990,"logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","blockTimestamp":10000,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
|
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
|
||||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
|
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","blockTimestamp":9990,"logIndex":"0x0","removed":false}]`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
|
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
|
||||||
|
|
|
||||||
|
|
@ -889,11 +889,11 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee())
|
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txctx := &Context{
|
txctx := &Context{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
BlockNumber: block.Number(),
|
BlockNumber: block.Number(),
|
||||||
|
|
@ -1041,7 +1041,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
||||||
|
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
||||||
_, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm)
|
_, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, &usedGas, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ func TestInternals(t *testing.T) {
|
||||||
byte(vm.LOG0),
|
byte(vm.LOG0),
|
||||||
},
|
},
|
||||||
tracer: mkTracer("prestateTracer", nil),
|
tracer: mkTracer("prestateTracer", nil),
|
||||||
want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex),
|
want: fmt.Sprintf(`{"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// CREATE2 which requires padding memory by prestate tracer
|
// CREATE2 which requires padding memory by prestate tracer
|
||||||
|
|
@ -340,7 +340,7 @@ func TestInternals(t *testing.T) {
|
||||||
byte(vm.LOG0),
|
byte(vm.LOG0),
|
||||||
},
|
},
|
||||||
tracer: mkTracer("prestateTracer", nil),
|
tracer: mkTracer("prestateTracer", nil),
|
||||||
want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600160ff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex),
|
want: fmt.Sprintf(`{"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600160ff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@
|
||||||
},
|
},
|
||||||
"result": {
|
"result": {
|
||||||
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||||
"balance": "0x0",
|
"balance":"0x0",
|
||||||
"nonce": 22
|
"nonce":22
|
||||||
},
|
},
|
||||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||||
"balance": "0x4d87094125a369d9bd5",
|
"balance": "0x4d87094125a369d9bd5",
|
||||||
|
|
@ -75,9 +75,6 @@
|
||||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
"balance": "0x1780d77678137ac1b775",
|
"balance": "0x1780d77678137ac1b775",
|
||||||
"nonce": 29072
|
"nonce": 29072
|
||||||
},
|
|
||||||
"0x1585936b53834b021f68cc13eeefdec2efc8e724": {
|
|
||||||
"balance": "0x0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,6 @@
|
||||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
"balance": "0x1780d77678137ac1b775",
|
"balance": "0x1780d77678137ac1b775",
|
||||||
"nonce": 29072
|
"nonce": 29072
|
||||||
},
|
|
||||||
"0x1585936b53834b021f68cc13eeefdec2efc8e724": {
|
|
||||||
"balance": "0x0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,6 @@
|
||||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
"balance": "0x1780d77678137ac1b775",
|
"balance": "0x1780d77678137ac1b775",
|
||||||
"nonce": 29072
|
"nonce": 29072
|
||||||
},
|
|
||||||
"0x1585936b53834b021f68cc13eeefdec2efc8e724": {
|
|
||||||
"balance": "0x0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
eth/tracers/internal/tracetest/testdata/prestate_tracer/enable_empty.json
vendored
Normal file
61
eth/tracers/internal/tracetest/testdata/prestate_tracer/enable_empty.json
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"difficulty": "3755480783",
|
||||||
|
"gasLimit": "5401723",
|
||||||
|
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||||
|
"number": "2294702",
|
||||||
|
"timestamp": "1513676146"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"alloc": {
|
||||||
|
"0x13e4acefe6a6700604929946e70e6443e4e73447": {
|
||||||
|
"balance": "0xcf3e0938579f000",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "9",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"byzantiumBlock": 1700000,
|
||||||
|
"chainId": 3,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||||
|
"eip155Block": 10,
|
||||||
|
"eip158Block": 10,
|
||||||
|
"ethash": {},
|
||||||
|
"homesteadBlock": 0
|
||||||
|
},
|
||||||
|
"difficulty": "3757315409",
|
||||||
|
"extraData": "0x566961425443",
|
||||||
|
"gasLimit": "5406414",
|
||||||
|
"hash": "0xae107f592eebdd9ff8d6ba00363676096e6afb0e1007a7d3d0af88173077378d",
|
||||||
|
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||||
|
"mixHash": "0xc927aa05a38bc3de864e95c33b3ae559d3f39c4ccd51cef6f113f9c50ba0caf1",
|
||||||
|
"nonce": "0x93363bbd2c95f410",
|
||||||
|
"number": "2294701",
|
||||||
|
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||||
|
"timestamp": "1513676127"
|
||||||
|
},
|
||||||
|
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||||
|
"tracerConfig": {
|
||||||
|
"includeEmpty": true
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"0x13e4acefe6a6700604929946e70e6443e4e73447": {
|
||||||
|
"balance": "0xcf3e0938579f000",
|
||||||
|
"nonce": 9
|
||||||
|
},
|
||||||
|
"0x7dc9c9730689ff0b0fd506c67db815f12d90a448": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xd049bfd667cb46aa3ef5df0da3e57db3be39e511": {
|
||||||
|
"balance": "0x0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
"balance": "0x1780d77678137ac1b775",
|
"balance": "0x1780d77678137ac1b775",
|
||||||
"code": "0x",
|
"code": "0x",
|
||||||
"nonce": "29072",
|
"nonce": 29072,
|
||||||
"storage": {}
|
"storage": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -74,9 +74,6 @@
|
||||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
"balance": "0x1780d77678137ac1b775",
|
"balance": "0x1780d77678137ac1b775",
|
||||||
"nonce": 29072
|
"nonce": 29072
|
||||||
},
|
|
||||||
"0x1585936b53834b021f68cc13eeefdec2efc8e724": {
|
|
||||||
"balance": "0x0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,6 @@
|
||||||
"balance": "0x0",
|
"balance": "0x0",
|
||||||
"nonce": 22
|
"nonce": 22
|
||||||
},
|
},
|
||||||
"0x1585936b53834b021f68cc13eeefdec2efc8e724": {
|
|
||||||
"balance": "0x0"
|
|
||||||
},
|
|
||||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||||
"balance": "0x4d87094125a369d9bd5",
|
"balance": "0x4d87094125a369d9bd5",
|
||||||
"nonce": 1,
|
"nonce": 1,
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,6 @@
|
||||||
"balance": "0x0",
|
"balance": "0x0",
|
||||||
"nonce": 22
|
"nonce": 22
|
||||||
},
|
},
|
||||||
"0x1585936b53834b021f68cc13eeefdec2efc8e724": {
|
|
||||||
"balance": "0x0"
|
|
||||||
},
|
|
||||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||||
"balance": "0x4d87094125a369d9bd5",
|
"balance": "0x4d87094125a369d9bd5",
|
||||||
"nonce": 1,
|
"nonce": 1,
|
||||||
|
|
|
||||||
|
|
@ -179,8 +179,12 @@ func (s *StructLog) toLegacyJSON() json.RawMessage {
|
||||||
}
|
}
|
||||||
if len(s.Memory) > 0 {
|
if len(s.Memory) > 0 {
|
||||||
memory := make([]string, 0, (len(s.Memory)+31)/32)
|
memory := make([]string, 0, (len(s.Memory)+31)/32)
|
||||||
for i := 0; i+32 <= len(s.Memory); i += 32 {
|
for i := 0; i < len(s.Memory); i += 32 {
|
||||||
memory = append(memory, fmt.Sprintf("%x", s.Memory[i:i+32]))
|
end := i + 32
|
||||||
|
if end > len(s.Memory) {
|
||||||
|
end = len(s.Memory)
|
||||||
|
}
|
||||||
|
memory = append(memory, fmt.Sprintf("%x", s.Memory[i:end]))
|
||||||
}
|
}
|
||||||
msg.Memory = &memory
|
msg.Memory = &memory
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package native
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
|
@ -75,6 +76,7 @@ type prestateTracerConfig struct {
|
||||||
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
||||||
DisableCode bool `json:"disableCode"` // If true, this tracer will not return the contract code
|
DisableCode bool `json:"disableCode"` // If true, this tracer will not return the contract code
|
||||||
DisableStorage bool `json:"disableStorage"` // If true, this tracer will not return the contract storage
|
DisableStorage bool `json:"disableStorage"` // If true, this tracer will not return the contract storage
|
||||||
|
IncludeEmpty bool `json:"includeEmpty"` // If true, this tracer will return empty state objects
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
|
|
@ -82,6 +84,11 @@ func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *p
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Diff mode has special semantics around account creating and deletion which
|
||||||
|
// requires it to include empty accounts and storage.
|
||||||
|
if config.DiffMode && config.IncludeEmpty {
|
||||||
|
return nil, errors.New("cannot use diffMode with includeEmpty")
|
||||||
|
}
|
||||||
t := &prestateTracer{
|
t := &prestateTracer{
|
||||||
pre: stateMap{},
|
pre: stateMap{},
|
||||||
post: stateMap{},
|
post: stateMap{},
|
||||||
|
|
@ -177,11 +184,14 @@ func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
if t.config.DiffMode {
|
if t.config.DiffMode {
|
||||||
t.processDiffState()
|
t.processDiffState()
|
||||||
}
|
}
|
||||||
// the new created contracts' prestate were empty, so delete them
|
// Remove accounts that were empty prior to execution. Unless
|
||||||
for a := range t.created {
|
// user requested to include empty accounts.
|
||||||
// the created contract maybe exists in statedb before the creating tx
|
if t.config.IncludeEmpty {
|
||||||
if s := t.pre[a]; s != nil && s.empty {
|
return
|
||||||
delete(t.pre, a)
|
}
|
||||||
|
for addr, s := range t.pre {
|
||||||
|
if s.empty {
|
||||||
|
delete(t.pre, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"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/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
@ -204,6 +205,17 @@ func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- co
|
||||||
return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
|
return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||||
|
// and returns them as a JSON object.
|
||||||
|
func (ec *Client) TraceTransaction(ctx context.Context, hash common.Hash, config *tracers.TraceConfig) (any, error) {
|
||||||
|
var result any
|
||||||
|
err := ec.c.CallContext(ctx, &result, "debug_traceTransaction", hash.Hex(), config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func toBlockNumArg(number *big.Int) string {
|
func toBlockNumArg(number *big.Int) string {
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return "latest"
|
return "latest"
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -47,13 +48,16 @@ var (
|
||||||
testSlot = common.HexToHash("0xdeadbeef")
|
testSlot = common.HexToHash("0xdeadbeef")
|
||||||
testValue = crypto.Keccak256Hash(testSlot[:])
|
testValue = crypto.Keccak256Hash(testSlot[:])
|
||||||
testBalance = big.NewInt(2e15)
|
testBalance = big.NewInt(2e15)
|
||||||
|
testTxHashes []common.Hash
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
||||||
// Generate test chain.
|
// Generate test chain.
|
||||||
genesis, blocks := generateTestChain()
|
genesis, blocks := generateTestChain()
|
||||||
// Create node
|
// Create node
|
||||||
n, err := node.New(&node.Config{})
|
n, err := node.New(&node.Config{
|
||||||
|
HTTPModules: []string{"debug", "eth", "admin"},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can't create new node: %v", err)
|
t.Fatalf("can't create new node: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -63,6 +67,8 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can't create new ethereum service: %v", err)
|
t.Fatalf("can't create new ethereum service: %v", err)
|
||||||
}
|
}
|
||||||
|
n.RegisterAPIs(tracers.APIs(ethservice.APIBackend))
|
||||||
|
|
||||||
filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{})
|
filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{})
|
||||||
n.RegisterAPIs([]rpc.API{{
|
n.RegisterAPIs([]rpc.API{{
|
||||||
Namespace: "eth",
|
Namespace: "eth",
|
||||||
|
|
@ -93,6 +99,19 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
|
||||||
generate := func(i int, g *core.BlockGen) {
|
generate := func(i int, g *core.BlockGen) {
|
||||||
g.OffsetTime(5)
|
g.OffsetTime(5)
|
||||||
g.SetExtra([]byte("test"))
|
g.SetExtra([]byte("test"))
|
||||||
|
|
||||||
|
to := common.BytesToAddress([]byte{byte(i + 1)})
|
||||||
|
tx := types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: uint64(i),
|
||||||
|
To: &to,
|
||||||
|
Value: big.NewInt(int64(2*i + 1)),
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasPrice: big.NewInt(params.InitialBaseFee),
|
||||||
|
Data: nil,
|
||||||
|
})
|
||||||
|
tx, _ = types.SignTx(tx, types.LatestSignerForChainID(genesis.Config.ChainID), testKey)
|
||||||
|
g.AddTx(tx)
|
||||||
|
testTxHashes = append(testTxHashes, tx.Hash())
|
||||||
}
|
}
|
||||||
_, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 1, generate)
|
_, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 1, generate)
|
||||||
blocks = append([]*types.Block{genesis.ToBlock()}, blocks...)
|
blocks = append([]*types.Block{genesis.ToBlock()}, blocks...)
|
||||||
|
|
@ -136,9 +155,6 @@ func TestGethClient(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
"TestSubscribePendingTxHashes",
|
"TestSubscribePendingTxHashes",
|
||||||
func(t *testing.T) { testSubscribePendingTransactions(t, client) },
|
func(t *testing.T) { testSubscribePendingTransactions(t, client) },
|
||||||
}, {
|
|
||||||
"TestSubscribePendingTxs",
|
|
||||||
func(t *testing.T) { testSubscribeFullPendingTransactions(t, client) },
|
|
||||||
}, {
|
}, {
|
||||||
"TestCallContract",
|
"TestCallContract",
|
||||||
func(t *testing.T) { testCallContract(t, client) },
|
func(t *testing.T) { testCallContract(t, client) },
|
||||||
|
|
@ -153,7 +169,12 @@ func TestGethClient(t *testing.T) {
|
||||||
{
|
{
|
||||||
"TestAccessList",
|
"TestAccessList",
|
||||||
func(t *testing.T) { testAccessList(t, client) },
|
func(t *testing.T) { testAccessList(t, client) },
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
|
"TestTraceTransaction",
|
||||||
|
func(t *testing.T) { testTraceTransactions(t, client) },
|
||||||
|
},
|
||||||
|
{
|
||||||
"TestSetHead",
|
"TestSetHead",
|
||||||
func(t *testing.T) { testSetHead(t, client) },
|
func(t *testing.T) { testSetHead(t, client) },
|
||||||
},
|
},
|
||||||
|
|
@ -197,7 +218,7 @@ func testAccessList(t *testing.T, client *rpc.Client) {
|
||||||
wantVMErr: "execution reverted",
|
wantVMErr: "execution reverted",
|
||||||
wantAL: `[
|
wantAL: `[
|
||||||
{
|
{
|
||||||
"address": "0x3a220f351252089d385b29beca14e27f204c296a",
|
"address": "0xdb7d6ab1f17c6b31909ae466702703daef9269cf",
|
||||||
"storageKeys": [
|
"storageKeys": [
|
||||||
"0x0000000000000000000000000000000000000000000000000000000000000081"
|
"0x0000000000000000000000000000000000000000000000000000000000000081"
|
||||||
]
|
]
|
||||||
|
|
@ -389,16 +410,26 @@ func testSetHead(t *testing.T, client *rpc.Client) {
|
||||||
func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) {
|
func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) {
|
||||||
ec := New(client)
|
ec := New(client)
|
||||||
ethcl := ethclient.NewClient(client)
|
ethcl := ethclient.NewClient(client)
|
||||||
|
|
||||||
// Subscribe to Transactions
|
// Subscribe to Transactions
|
||||||
ch := make(chan common.Hash)
|
ch1 := make(chan common.Hash)
|
||||||
ec.SubscribePendingTransactions(context.Background(), ch)
|
ec.SubscribePendingTransactions(context.Background(), ch1)
|
||||||
|
|
||||||
|
// Subscribe to Transactions
|
||||||
|
ch2 := make(chan *types.Transaction)
|
||||||
|
ec.SubscribeFullPendingTransactions(context.Background(), ch2)
|
||||||
|
|
||||||
// Send a transaction
|
// Send a transaction
|
||||||
chainID, err := ethcl.ChainID(context.Background())
|
chainID, err := ethcl.ChainID(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
nonce, err := ethcl.NonceAt(context.Background(), testAddr, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
// Create transaction
|
// Create transaction
|
||||||
tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil)
|
tx := types.NewTransaction(nonce, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil)
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
signer := types.LatestSignerForChainID(chainID)
|
||||||
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
|
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -414,41 +445,12 @@ func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Check that the transaction was sent over the channel
|
// Check that the transaction was sent over the channel
|
||||||
hash := <-ch
|
hash := <-ch1
|
||||||
if hash != signedTx.Hash() {
|
if hash != signedTx.Hash() {
|
||||||
t.Fatalf("Invalid tx hash received, got %v, want %v", hash, signedTx.Hash())
|
t.Fatalf("Invalid tx hash received, got %v, want %v", hash, signedTx.Hash())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func testSubscribeFullPendingTransactions(t *testing.T, client *rpc.Client) {
|
|
||||||
ec := New(client)
|
|
||||||
ethcl := ethclient.NewClient(client)
|
|
||||||
// Subscribe to Transactions
|
|
||||||
ch := make(chan *types.Transaction)
|
|
||||||
ec.SubscribeFullPendingTransactions(context.Background(), ch)
|
|
||||||
// Send a transaction
|
|
||||||
chainID, err := ethcl.ChainID(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Create transaction
|
|
||||||
tx := types.NewTransaction(1, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil)
|
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
|
||||||
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
signedTx, err := tx.WithSignature(signer, signature)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Send transaction
|
|
||||||
err = ethcl.SendTransaction(context.Background(), signedTx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Check that the transaction was sent over the channel
|
// Check that the transaction was sent over the channel
|
||||||
tx = <-ch
|
tx = <-ch2
|
||||||
if tx.Hash() != signedTx.Hash() {
|
if tx.Hash() != signedTx.Hash() {
|
||||||
t.Fatalf("Invalid tx hash received, got %v, want %v", tx.Hash(), signedTx.Hash())
|
t.Fatalf("Invalid tx hash received, got %v, want %v", tx.Hash(), signedTx.Hash())
|
||||||
}
|
}
|
||||||
|
|
@ -478,6 +480,25 @@ func testCallContract(t *testing.T, client *rpc.Client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTraceTransactions(t *testing.T, client *rpc.Client) {
|
||||||
|
ec := New(client)
|
||||||
|
for _, txHash := range testTxHashes {
|
||||||
|
// Struct logger
|
||||||
|
_, err := ec.TraceTransaction(context.Background(), txHash, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct logger
|
||||||
|
_, err = ec.TraceTransaction(context.Background(), txHash,
|
||||||
|
&tracers.TraceConfig{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestOverrideAccountMarshal(t *testing.T) {
|
func TestOverrideAccountMarshal(t *testing.T) {
|
||||||
om := map[common.Address]OverrideAccount{
|
om := map[common.Address]OverrideAccount{
|
||||||
{0x11}: {
|
{0x11}: {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue