mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge branch 'ethereum:master' into codespace-legendary-space-disco-q7pwg95px5gx2xpqj
This commit is contained in:
commit
b590b5f0e5
197 changed files with 14168 additions and 2920 deletions
7
.mailmap
7
.mailmap
|
|
@ -50,6 +50,10 @@ Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
|
||||||
|
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
|
|
||||||
|
Charlotte <tqpcharlie@proton.me>
|
||||||
|
Charlotte <tqpcharlie@proton.me> <Zachinquarantine@protonmail.com>
|
||||||
|
Charlotte <tqpcharlie@proton.me> <zachinquarantine@yahoo.com>
|
||||||
|
|
||||||
Cheng Li <lob4tt@gmail.com>
|
Cheng Li <lob4tt@gmail.com>
|
||||||
|
|
||||||
Chris Ziogas <ziogaschr@gmail.com>
|
Chris Ziogas <ziogaschr@gmail.com>
|
||||||
|
|
@ -301,9 +305,6 @@ Yohann Léon <sybiload@gmail.com>
|
||||||
yzb <335357057@qq.com>
|
yzb <335357057@qq.com>
|
||||||
yzb <335357057@qq.com> <flyingyzb@gmail.com>
|
yzb <335357057@qq.com> <flyingyzb@gmail.com>
|
||||||
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com> <zachinquarantine@yahoo.com>
|
|
||||||
|
|
||||||
Ziyuan Zhong <zzy.albert@163.com>
|
Ziyuan Zhong <zzy.albert@163.com>
|
||||||
|
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
|
|
|
||||||
2
AUTHORS
2
AUTHORS
|
|
@ -123,6 +123,7 @@ Ceyhun Onur <ceyhun.onur@avalabs.org>
|
||||||
chabashilah <doumodoumo@gmail.com>
|
chabashilah <doumodoumo@gmail.com>
|
||||||
changhong <changhong.yu@shanbay.com>
|
changhong <changhong.yu@shanbay.com>
|
||||||
Charles Cooper <cooper.charles.m@gmail.com>
|
Charles Cooper <cooper.charles.m@gmail.com>
|
||||||
|
Charlotte <tqpcharlie@proton.me>
|
||||||
Chase Wright <mysticryuujin@gmail.com>
|
Chase Wright <mysticryuujin@gmail.com>
|
||||||
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
Chawin Aiemvaravutigul <nick41746@hotmail.com>
|
||||||
Chen Quan <terasum@163.com>
|
Chen Quan <terasum@163.com>
|
||||||
|
|
@ -839,7 +840,6 @@ ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
|
||||||
yzb <335357057@qq.com>
|
yzb <335357057@qq.com>
|
||||||
zaccoding <zaccoding725@gmail.com>
|
zaccoding <zaccoding725@gmail.com>
|
||||||
Zach <zach.ramsay@gmail.com>
|
Zach <zach.ramsay@gmail.com>
|
||||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
|
||||||
zah <zahary@gmail.com>
|
zah <zahary@gmail.com>
|
||||||
Zahoor Mohamed <zahoor@zahoor.in>
|
Zahoor Mohamed <zahoor@zahoor.in>
|
||||||
Zak Cole <zak@beattiecole.com>
|
Zak Cole <zak@beattiecole.com>
|
||||||
|
|
|
||||||
|
|
@ -939,6 +939,7 @@ var bindTests = []struct {
|
||||||
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{byte(j)}, [32]byte{byte(j)}, true, big.NewInt(int64(10*i+j))); err != nil {
|
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{byte(j)}, [32]byte{byte(j)}, true, big.NewInt(int64(10*i+j))); err != nil {
|
||||||
t.Fatalf("block %d, event %d: raise failed: %v", i, j, err)
|
t.Fatalf("block %d, event %d: raise failed: %v", i, j, err)
|
||||||
}
|
}
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
}
|
}
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
}
|
}
|
||||||
|
|
@ -1495,7 +1496,7 @@ var bindTests = []struct {
|
||||||
if n != 3 {
|
if n != 3 {
|
||||||
t.Fatalf("Invalid bar0 event")
|
t.Fatalf("Invalid bar0 event")
|
||||||
}
|
}
|
||||||
case <-time.NewTimer(3 * time.Second).C:
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
t.Fatalf("Wait bar0 event timeout")
|
t.Fatalf("Wait bar0 event timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1506,7 +1507,7 @@ var bindTests = []struct {
|
||||||
if n != 1 {
|
if n != 1 {
|
||||||
t.Fatalf("Invalid bar event")
|
t.Fatalf("Invalid bar event")
|
||||||
}
|
}
|
||||||
case <-time.NewTimer(3 * time.Second).C:
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
t.Fatalf("Wait bar event timeout")
|
t.Fatalf("Wait bar event timeout")
|
||||||
}
|
}
|
||||||
close(stopCh)
|
close(stopCh)
|
||||||
|
|
|
||||||
116
accounts/abi/abigen/testdata/v2/structs-abi.go.txt
vendored
116
accounts/abi/abigen/testdata/v2/structs-abi.go.txt
vendored
|
|
@ -1,116 +0,0 @@
|
||||||
// Code generated via abigen V2 - DO NOT EDIT.
|
|
||||||
// This file is a generated binding and any manual changes will be lost.
|
|
||||||
|
|
||||||
package v1bindtests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var (
|
|
||||||
_ = bytes.Equal
|
|
||||||
_ = errors.New
|
|
||||||
_ = big.NewInt
|
|
||||||
_ = common.Big1
|
|
||||||
_ = types.BloomLookup
|
|
||||||
_ = abi.ConvertType
|
|
||||||
)
|
|
||||||
|
|
||||||
// Struct0 is an auto generated low-level Go binding around an user-defined struct.
|
|
||||||
type Struct0 struct {
|
|
||||||
B [32]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructsMetaData contains all meta data concerning the Structs contract.
|
|
||||||
var StructsMetaData = bind.MetaData{
|
|
||||||
ABI: "[{\"inputs\":[],\"name\":\"F\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[]\",\"name\":\"c\",\"type\":\"uint256[]\"},{\"internalType\":\"bool[]\",\"name\":\"d\",\"type\":\"bool[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"G\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"B\",\"type\":\"bytes32\"}],\"internalType\":\"structStructs.A[]\",\"name\":\"a\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
|
|
||||||
ID: "Structs",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Structs is an auto generated Go binding around an Ethereum contract.
|
|
||||||
type Structs struct {
|
|
||||||
abi abi.ABI
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStructs creates a new instance of Structs.
|
|
||||||
func NewStructs() *Structs {
|
|
||||||
parsed, err := StructsMetaData.ParseABI()
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.New("invalid ABI: " + err.Error()))
|
|
||||||
}
|
|
||||||
return &Structs{abi: *parsed}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance creates a wrapper for a deployed contract instance at the given address.
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// Use this to create the instance object passed to abigen v2 library functions Call, Transact, etc.
|
|
||||||
func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) *bind.BoundContract {
|
|
||||||
return bind.NewBoundContract(addr, c.abi, backend, backend, backend)
|
|
||||||
=======
|
|
||||||
// Use this to create the instance object passed to abigen v2 library functions Call,
|
|
||||||
// Transact, etc.
|
|
||||||
func (c *Structs) Instance(backend bind.ContractBackend, addr common.Address) bind.BoundContract {
|
|
||||||
return bind.NewBoundContract(backend, addr, c.abi)
|
|
||||||
>>>>>>> 854c25e086 (accounts/abi/abigen: improve v2 template)
|
|
||||||
}
|
|
||||||
|
|
||||||
// F is the Go binding used to pack the parameters required for calling
|
|
||||||
// the contract method 0x28811f59.
|
|
||||||
//
|
|
||||||
// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d)
|
|
||||||
func (structs *Structs) PackF() ([]byte, error) {
|
|
||||||
return structs.abi.Pack("F")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FOutput serves as a container for the return parameters of contract
|
|
||||||
// method F.
|
|
||||||
type FOutput struct {
|
|
||||||
A []Struct0
|
|
||||||
C []*big.Int
|
|
||||||
D []bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackF is the Go binding that unpacks the parameters returned
|
|
||||||
// from invoking the contract method with ID 0x28811f59.
|
|
||||||
//
|
|
||||||
// Solidity: function F() view returns((bytes32)[] a, uint256[] c, bool[] d)
|
|
||||||
func (structs *Structs) UnpackF(data []byte) (*FOutput, error) {
|
|
||||||
out, err := structs.abi.Unpack("F", data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret := new(FOutput)
|
|
||||||
ret.A = *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0)
|
|
||||||
ret.C = *abi.ConvertType(out[1], new([]*big.Int)).(*[]*big.Int)
|
|
||||||
ret.D = *abi.ConvertType(out[2], new([]bool)).(*[]bool)
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// G is the Go binding used to pack the parameters required for calling
|
|
||||||
// the contract method 0x6fecb623.
|
|
||||||
//
|
|
||||||
// Solidity: function G() view returns((bytes32)[] a)
|
|
||||||
func (structs *Structs) PackG() ([]byte, error) {
|
|
||||||
return structs.abi.Pack("G")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackG is the Go binding that unpacks the parameters returned
|
|
||||||
// from invoking the contract method with ID 0x6fecb623.
|
|
||||||
//
|
|
||||||
// Solidity: function G() view returns((bytes32)[] a)
|
|
||||||
func (structs *Structs) UnpackG(data []byte) (*[]Struct0, error) {
|
|
||||||
out, err := structs.abi.Unpack("G", data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out0 := *abi.ConvertType(out[0], new([]Struct0)).(*[]Struct0)
|
|
||||||
return &out0, nil
|
|
||||||
}
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/beacon/params"
|
"github.com/ethereum/go-ethereum/beacon/params"
|
||||||
"github.com/ethereum/go-ethereum/beacon/types"
|
"github.com/ethereum/go-ethereum/beacon/types"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ctypes "github.com/ethereum/go-ethereum/core/types"
|
ctypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -104,7 +105,11 @@ func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent)
|
||||||
method = "engine_newPayloadV4"
|
method = "engine_newPayloadV4"
|
||||||
parentBeaconRoot := event.BeaconHead.ParentRoot
|
parentBeaconRoot := event.BeaconHead.ParentRoot
|
||||||
blobHashes := collectBlobHashes(event.Block)
|
blobHashes := collectBlobHashes(event.Block)
|
||||||
params = append(params, blobHashes, parentBeaconRoot, event.ExecRequests)
|
hexRequests := make([]hexutil.Bytes, len(event.ExecRequests))
|
||||||
|
for i := range event.ExecRequests {
|
||||||
|
hexRequests[i] = hexutil.Bytes(event.ExecRequests[i])
|
||||||
|
}
|
||||||
|
params = append(params, blobHashes, parentBeaconRoot, hexRequests)
|
||||||
case "deneb":
|
case "deneb":
|
||||||
method = "engine_newPayloadV3"
|
method = "engine_newPayloadV3"
|
||||||
parentBeaconRoot := event.BeaconHead.ParentRoot
|
parentBeaconRoot := event.BeaconHead.ParentRoot
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ type executionPayloadEnvelopeMarshaling struct {
|
||||||
|
|
||||||
type PayloadStatusV1 struct {
|
type PayloadStatusV1 struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Witness *hexutil.Bytes `json:"witness"`
|
Witness *hexutil.Bytes `json:"witness,omitempty"`
|
||||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||||
ValidationError *string `json:"validationError"`
|
ValidationError *string `json:"validationError"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
0xf5606a019f0f1006e9ec2070695045f4334450362a48da4c5965314510e0b4c3
|
0xd60e5310c5d52ced44cfb13be4e9f22a1e6a6dc56964c3cccd429182d26d72d0
|
||||||
|
|
@ -1 +1 @@
|
||||||
0x3c0cb4aa83beded1803d262664ba4392b1023f334d9cca02dc3a6925987ebe91
|
0x02f0bb348b0d45f95a9b7e2bb5705768ad06548876cee03d880a2c9dabb1ff88
|
||||||
|
|
@ -1 +1 @@
|
||||||
0xa8d56457aa414523d93659aef1f7409bbfb72ad75e94d917c8c0b1baa38153ef
|
0xa0dad451a230c01be6f2492980ec5bb412d8cf33351a75e8b172b5b84a5fd03a
|
||||||
|
|
@ -41,7 +41,8 @@ var (
|
||||||
AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}).
|
AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}).
|
||||||
AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}).
|
AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}).
|
||||||
AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}).
|
AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}).
|
||||||
AddFork("DENEB", 269568, []byte{4, 0, 0, 0})
|
AddFork("DENEB", 269568, []byte{4, 0, 0, 0}).
|
||||||
|
AddFork("ELECTRA", 364032, []byte{5, 0, 0, 0})
|
||||||
|
|
||||||
SepoliaLightConfig = (&ChainConfig{
|
SepoliaLightConfig = (&ChainConfig{
|
||||||
GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"),
|
GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"),
|
||||||
|
|
|
||||||
|
|
@ -5,54 +5,54 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/
|
||||||
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
b69211752a3029083c020dc635fe12156ca1a6725a08559da540a0337586a77e fixtures_pectra-devnet-6.tar.gz
|
||||||
|
|
||||||
# version:golang 1.24.2
|
# version:golang 1.24.3
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
9dc77ffadc16d837a1bf32d99c624cb4df0647cee7b119edd9e7b1bcc05f2e00 go1.24.2.src.tar.gz
|
229c08b600b1446798109fae1f569228102c8473caba8104b6418cb5bc032878 go1.24.3.src.tar.gz
|
||||||
427b373540d8fd51dbcc46bdecd340af109cd41514443c000d3dcde72b2c65a3 go1.24.2.aix-ppc64.tar.gz
|
6f6901497547db3b77c14f7f953fbcef9fa5fb84199ee2ee14a5686e66bed5a6 go1.24.3.aix-ppc64.tar.gz
|
||||||
238d9c065d09ff6af229d2e3b8b5e85e688318d69f4006fb85a96e41c216ea83 go1.24.2.darwin-amd64.tar.gz
|
a05fa7e4043a4fec66897135219e3b8ab2202b5ef351c60c2fbb531dfb8f2900 go1.24.3.darwin-amd64.pkg
|
||||||
535ed9ff283fee39575a7fb9b6d8b1901b6dc640d06dc71fd7d3faeefdaf8030 go1.24.2.darwin-amd64.pkg
|
13e6fe3fcf65689d77d40e633de1e31c6febbdbcb846eb05fc2434ed2213e92b go1.24.3.darwin-amd64.tar.gz
|
||||||
b70f8b3c5b4ccb0ad4ffa5ee91cd38075df20fdbd953a1daedd47f50fbcff47a go1.24.2.darwin-arm64.tar.gz
|
97055ff4214043b39dc32e043fdd5c565df7c0a4e2fc0174e779a134c347ae0e go1.24.3.darwin-arm64.pkg
|
||||||
4732f607a47ce4d898c0af01ff68f07e0820a6b50603aef5d5c777d1102505e2 go1.24.2.darwin-arm64.pkg
|
64a3fa22142f627e78fac3018ce3d4aeace68b743eff0afda8aae0411df5e4fb go1.24.3.darwin-arm64.tar.gz
|
||||||
c17686b5fd61a663fbfafccfa177961be59386cf294e935ce35866b9dcb8e78a go1.24.2.dragonfly-amd64.tar.gz
|
32de3fd44d5055973978436a7f1f0ffbaae85c1b603ec6105e5c38d8a674c721 go1.24.3.dragonfly-amd64.tar.gz
|
||||||
026f1dd906189acff714c7625686bbc4ed91042618ba010d45b671461acc9e63 go1.24.2.freebsd-386.tar.gz
|
9fe6101b3797919bd7337ee5ce591954f85d59db7ae88983904db29fd64c3dd1 go1.24.3.freebsd-386.tar.gz
|
||||||
49399ba759b570a8f87d12179133403da6c2dd296d63a8830dee309161b9c40c go1.24.2.freebsd-amd64.tar.gz
|
6ccf4cca287e90cc28cd7954b6172f5d177a17e20b072b65f7f39636c325e2fb go1.24.3.freebsd-amd64.tar.gz
|
||||||
1f48f47183794d97c29736004247ab541177cf984ac6322c78bc43828daa1172 go1.24.2.freebsd-arm.tar.gz
|
ce45ebf389066f82a7b056b66dd650efb51fde6f8bf92a2a3ab6990f02788ebf go1.24.3.freebsd-arm.tar.gz
|
||||||
ef856428b60a8c0bd9a2cba596e83024be6f1c2d5574e89cb1ff2262b08df8b9 go1.24.2.freebsd-arm64.tar.gz
|
8f6494a12a874d0ea57c67987829359e016960ce3ba0673273609d6ac2af589a go1.24.3.freebsd-arm64.tar.gz
|
||||||
ec2088823e16df00600a6d0f72e9a7dc6d2f80c9c140c2043c0cf20e1404d1a9 go1.24.2.freebsd-riscv64.tar.gz
|
f9db392560cf0851f0bc8f2190e1978e01b4603038c27fecfc8658a695b71616 go1.24.3.freebsd-riscv64.tar.gz
|
||||||
e030e7cedbb8688f1d75cb80f3de6ee2e6617a67d34051e794e5992b53462147 go1.24.2.illumos-amd64.tar.gz
|
01717fff64c5d98457272002fa825d0a15e307bf6e189f2b0c23817fa033b61c go1.24.3.illumos-amd64.tar.gz
|
||||||
4c382776d52313266f3026236297a224a6688751256a2dffa3f524d8d6f6c0ba go1.24.2.linux-386.tar.gz
|
41b1051063e68cbd2b919bf12326764fe33937cf1d32b5c529dd1a4f43dce578 go1.24.3.linux-386.tar.gz
|
||||||
68097bd680839cbc9d464a0edce4f7c333975e27a90246890e9f1078c7e702ad go1.24.2.linux-amd64.tar.gz
|
3333f6ea53afa971e9078895eaa4ac7204a8c6b5c68c10e6bc9a33e8e391bdd8 go1.24.3.linux-amd64.tar.gz
|
||||||
756274ea4b68fa5535eb9fe2559889287d725a8da63c6aae4d5f23778c229f4b go1.24.2.linux-arm64.tar.gz
|
a463cb59382bd7ae7d8f4c68846e73c4d589f223c589ac76871b66811ded7836 go1.24.3.linux-arm64.tar.gz
|
||||||
438d5d3d7dcb239b58d893a715672eabe670b9730b1fd1c8fc858a46722a598a go1.24.2.linux-armv6l.tar.gz
|
17a392d7e826625dd12a32099df0b00b85c32d8132ed86fe917183ee5c3f88ed go1.24.3.linux-armv6l.tar.gz
|
||||||
6aefd3bf59c3c5592eda4fb287322207f119c2210f3795afa9be48d3ccb73e1b go1.24.2.linux-loong64.tar.gz
|
e4b003c04c902edc140153d279b42167f1ad7c229f48f1f729bbef5e65e88d1f go1.24.3.linux-loong64.tar.gz
|
||||||
93e49bb4692783b0e9a2deab9558c6e8d2867f35592aeff285adda60924167f3 go1.24.2.linux-mips.tar.gz
|
1c79d89edf835edf9d4336ccea7cb89bc5c0ca82b12b36b218d599a5400d60fe go1.24.3.linux-mips.tar.gz
|
||||||
6e86e703675016f3faf6604b8f68f20dc1bba75849136e6dd4f43f69c8a4a9d9 go1.24.2.linux-mips64.tar.gz
|
0b64fe147d69f4d681d8e8a035c760477531432f83d831f18d37cb9bf3652488 go1.24.3.linux-mips64.tar.gz
|
||||||
f233d237538ca1559a7d7cf519a29f0147923a951377bc4e467af4c059e68851 go1.24.2.linux-mips64le.tar.gz
|
396b784c255b64512dc00c302c053e43a3cbfc77518664c6ac5569aafad4d1e6 go1.24.3.linux-mips64le.tar.gz
|
||||||
545e1b9a7939f923fd53bde98334b987ef42eb353ee3e0bfede8aa06079d6b24 go1.24.2.linux-mipsle.tar.gz
|
93898313887f14e8efbe9d7386d5da4792b2d6c492bee562993fd4c9daa75c6d go1.24.3.linux-mipsle.tar.gz
|
||||||
6eab31481f2f46187bc1b6c887662eef06fc9d7271a8390854072cdb387c8d74 go1.24.2.linux-ppc64.tar.gz
|
873ae3a6a6655a7b6f820e095d9965507e8dfd3cf76bc92d75c564ecbca385f6 go1.24.3.linux-ppc64.tar.gz
|
||||||
5fff857791d541c71d8ea0171c73f6f99770d15ff7e2ad979104856d01f36563 go1.24.2.linux-ppc64le.tar.gz
|
341a749d168f47b1d4dad25e32cae70849b7ceed7c290823b853c9e6b0df0856 go1.24.3.linux-ppc64le.tar.gz
|
||||||
91bda1558fcbd1c92769ad86c8f5cf796f8c67b0d9d9c19f76eecfc75ce71527 go1.24.2.linux-riscv64.tar.gz
|
fa482f53ccb4ba280316b8c5751ea67291507280d9166f2a38fe4d9b5d5fb64b go1.24.3.linux-riscv64.tar.gz
|
||||||
1cb3448166d6abb515a85a3ee5afbdf932081fb58ad7143a8fb666fbc06146d9 go1.24.2.linux-s390x.tar.gz
|
a87b0c2a079a0bece1620fb29a00e02b4dba17507850f837e754af7d57cda282 go1.24.3.linux-s390x.tar.gz
|
||||||
a9a2c0db2e826f20f00b02bee01dfdaeb49591c2f6ffacb78dc64a950894f7ff go1.24.2.netbsd-386.tar.gz
|
63155382308db1306200aff7821aa26bf2a2dda23537dd637a9704b485b6ddf0 go1.24.3.netbsd-386.tar.gz
|
||||||
cd1a35b76ed9c7b6c0c1616741bd319699a77867ade0be9924f32496c0a87a3f go1.24.2.netbsd-amd64.tar.gz
|
fe2c5c79482958b867c08a4fc2a10a998de9c0206b08d5b3ebcb2232e8d2777c go1.24.3.netbsd-amd64.tar.gz
|
||||||
8c666388d066e479155cc5116950eeb435df28087ef277c18f1dc7479f836e60 go1.24.2.netbsd-arm.tar.gz
|
e8ff77aef21521b5dd94e44282a3243309b80717414cf12f72835a45886a049f go1.24.3.netbsd-arm.tar.gz
|
||||||
5d42f0be04f58da5be788a1e260f8747c316b8ce182bf0b273c2e4c691feaa1a go1.24.2.netbsd-arm64.tar.gz
|
b337fbaf82822685940ffaa76fbcf4be5d2f0258bc819cd80bc408b491f45c04 go1.24.3.netbsd-arm64.tar.gz
|
||||||
688effa23ea3973cc8b0fdf4246712cbeef55ff20c45f3a9e28b0c2db04246cf go1.24.2.openbsd-386.tar.gz
|
c1bb9dd8418480aa7f65452b08de3759da3bf89702be71b5a9fc084836b24ad5 go1.24.3.openbsd-386.tar.gz
|
||||||
e5daf95f1048d8026b1366450a3f8044d668b0639db6422ad9a83755c6745cf7 go1.24.2.openbsd-amd64.tar.gz
|
531218de748b0caaf6d1ad18921206fc12baaa89bf483a0a5e60a571c206fe6f go1.24.3.openbsd-amd64.tar.gz
|
||||||
aeadaf74bd544d1a12ba9b14c0e7cdb1964de3ba9a52acb4619e91dbae7def7b go1.24.2.openbsd-arm.tar.gz
|
bcd0dc959986fc346969b5d4111c3c8031882d8bf8d87a2c2ecf1328962a91f2 go1.24.3.openbsd-arm.tar.gz
|
||||||
9e222d9adb0ce836a5b3c8d5aadbd167c8869c030b113f4a81aa88e9a200f279 go1.24.2.openbsd-arm64.tar.gz
|
00ee6f8f1c41fd2e28ad386bd7e39acce7cab84af6de835855b29d1c597335c4 go1.24.3.openbsd-arm64.tar.gz
|
||||||
192fffa34536adc3cd1bb7c1ee785b8bc156ae7afd10bbf5db99ec8f2e93066e go1.24.2.openbsd-ppc64.tar.gz
|
9f4ec0a9203ed3c54ce1a2a390ad3d45838cdb7efd85baeff857e37dfde04edd go1.24.3.openbsd-ppc64.tar.gz
|
||||||
a23e90b451a390549042c2a7efbec6f29ed98b2d5618c8d2a35704e21be96e09 go1.24.2.openbsd-riscv64.tar.gz
|
da4d6f80e2373250d8c31c32dcd1e08775c327c0d610923604660cc0e07e8cba go1.24.3.openbsd-riscv64.tar.gz
|
||||||
5cdcafe455d859b02779611a5a1e1d63e498b922e05818fb3debe410a5959e9e go1.24.2.plan9-386.tar.gz
|
f5d02149132eedda6c2d46b360d7da462b8a5f9e3f8567db100c2d7bff0ddcd7 go1.24.3.plan9-386.tar.gz
|
||||||
81351659804fa505c1b3ec6fdf9599f7f88df08614307eeb96071bf5e2e74beb go1.24.2.plan9-amd64.tar.gz
|
175f3d79f4762a3c545d2c6393bf6b8bac24e838026869dafab06b930735c94f go1.24.3.plan9-amd64.tar.gz
|
||||||
6e337d5def14ed0123423c1c32e2e6d8b19161e5d5ffaa7356dad48ee0fd80b4 go1.24.2.plan9-arm.tar.gz
|
d1e4ac15095da1611659261c2228c2058756cf87d61d9fad262f76755ef26849 go1.24.3.plan9-arm.tar.gz
|
||||||
07e6926ebc476c044d7d5b17706abfc52be52bccc2073d1734174efe63c6b35e go1.24.2.solaris-amd64.tar.gz
|
e644220a6ced3c07a7acc1364193cb709a97737dd8b6792a07a8ec6d9996713e go1.24.3.solaris-amd64.tar.gz
|
||||||
13d86cb818bba331da75fcd18246ab31a1067b44fb4a243b6dfd93097eda7f37 go1.24.2.windows-386.zip
|
0d7e7dc0a31ba0cdd487415709d03b02fc9490ef111e8dfd22788a6d63316f37 go1.24.3.windows-386.msi
|
||||||
8a702d9f7104a15bd935f4191c58c24c0b6389e066b9d5661b93915114a2bef0 go1.24.2.windows-386.msi
|
c27c463a61ab849266baa0c17a6c5c4256a574ab642f609ba25c96ec965dc184 go1.24.3.windows-386.zip
|
||||||
29c553aabee0743e2ffa3e9fa0cda00ef3b3cc4ff0bc92007f31f80fd69892e1 go1.24.2.windows-amd64.zip
|
d5b7637e7e138be877d96a4501709d480e050d86a8f402bc950e72112b5aedc5 go1.24.3.windows-amd64.msi
|
||||||
acefb191e72fea0bdb1a3f5f8f6f5ab18b42b3bbce0c7183f189f25953aff275 go1.24.2.windows-amd64.msi
|
be9787cb08998b1860fe3513e48a5fe5b96302d358a321b58e651184fa9638b3 go1.24.3.windows-amd64.zip
|
||||||
ab267f7f9a3366d48d7664be9e627ce3e63273231430cce5f7783fb910f14148 go1.24.2.windows-arm64.zip
|
7efde2e5e8468e9caf2c7fc94f4da78a726a5031a1ed63acff7899527cdddff6 go1.24.3.windows-arm64.msi
|
||||||
d187bfe539356c39573d2f46766d1d08122b4f33da00fd14d12485fa9e241ff5 go1.24.2.windows-arm64.msi
|
eec9fa736056b54dd88ecb669db2bfad39b0c48f6f9080f036dfa1ca42dc4bb5 go1.24.3.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 2.0.2
|
# version:golangci 2.0.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
55
build/ci.go
55
build/ci.go
|
|
@ -59,6 +59,7 @@ import (
|
||||||
"github.com/cespare/cp"
|
"github.com/cespare/cp"
|
||||||
"github.com/ethereum/go-ethereum/crypto/signify"
|
"github.com/ethereum/go-ethereum/crypto/signify"
|
||||||
"github.com/ethereum/go-ethereum/internal/build"
|
"github.com/ethereum/go-ethereum/internal/build"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/download"
|
||||||
"github.com/ethereum/go-ethereum/internal/version"
|
"github.com/ethereum/go-ethereum/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -190,7 +191,7 @@ func doInstall(cmdline []string) {
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
tc := build.GoToolchain{GOARCH: *arch, CC: *cc}
|
||||||
if *dlgo {
|
if *dlgo {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
tc.Root = build.DownloadGo(csdb)
|
tc.Root = build.DownloadGo(csdb)
|
||||||
}
|
}
|
||||||
// Disable CLI markdown doc generation in release builds.
|
// Disable CLI markdown doc generation in release builds.
|
||||||
|
|
@ -285,7 +286,7 @@ func doTest(cmdline []string) {
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
||||||
// Get test fixtures.
|
// Get test fixtures.
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
downloadSpecTestFixtures(csdb, *cachedir)
|
downloadSpecTestFixtures(csdb, *cachedir)
|
||||||
|
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
|
|
@ -329,16 +330,11 @@ func doTest(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
// downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures.
|
||||||
func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
|
||||||
executionSpecTestsVersion, err := build.Version(csdb, "spec-tests")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename
|
base := "fixtures_pectra-devnet-6" // TODO(s1na) rename once the version becomes part of the filename
|
||||||
url := fmt.Sprintf("https://github.com/ethereum/execution-spec-tests/releases/download/%s/%s%s", executionSpecTestsVersion, base, ext)
|
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
if err := build.ExtractArchive(archivePath, executionSpecTestsDir); err != nil {
|
||||||
|
|
@ -444,14 +440,13 @@ func doLint(cmdline []string) {
|
||||||
|
|
||||||
// downloadLinter downloads and unpacks golangci-lint.
|
// downloadLinter downloads and unpacks golangci-lint.
|
||||||
func downloadLinter(cachedir string) string {
|
func downloadLinter(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "golangci")
|
version, err := csdb.FindVersion("golangci")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
arch := runtime.GOARCH
|
arch := runtime.GOARCH
|
||||||
ext := ".tar.gz"
|
ext := ".tar.gz"
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
ext = ".zip"
|
ext = ".zip"
|
||||||
}
|
}
|
||||||
|
|
@ -459,9 +454,8 @@ func downloadLinter(cachedir string) string {
|
||||||
arch += "v" + os.Getenv("GOARM")
|
arch += "v" + os.Getenv("GOARM")
|
||||||
}
|
}
|
||||||
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
|
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, arch)
|
||||||
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s%s", version, base, ext)
|
|
||||||
archivePath := filepath.Join(cachedir, base+ext)
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
|
if err := build.ExtractArchive(archivePath, cachedir); err != nil {
|
||||||
|
|
@ -497,8 +491,8 @@ func protocArchiveBaseName() (string, error) {
|
||||||
// in the generate command. It returns the full path of the directory
|
// in the generate command. It returns the full path of the directory
|
||||||
// containing the 'protoc-gen-go' executable.
|
// containing the 'protoc-gen-go' executable.
|
||||||
func downloadProtocGenGo(cachedir string) string {
|
func downloadProtocGenGo(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "protoc-gen-go")
|
version, err := csdb.FindVersion("protoc-gen-go")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -510,10 +504,8 @@ func downloadProtocGenGo(cachedir string) string {
|
||||||
archiveName += ".tar.gz"
|
archiveName += ".tar.gz"
|
||||||
}
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName)
|
|
||||||
|
|
||||||
archivePath := path.Join(cachedir, archiveName)
|
archivePath := path.Join(cachedir, archiveName)
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
extractDest := filepath.Join(cachedir, baseName)
|
extractDest := filepath.Join(cachedir, baseName)
|
||||||
|
|
@ -531,8 +523,8 @@ func downloadProtocGenGo(cachedir string) string {
|
||||||
// files as a CI step. It returns the full path to the directory containing
|
// files as a CI step. It returns the full path to the directory containing
|
||||||
// the protoc executable.
|
// the protoc executable.
|
||||||
func downloadProtoc(cachedir string) string {
|
func downloadProtoc(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
version, err := build.Version(csdb, "protoc")
|
version, err := csdb.FindVersion("protoc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -543,10 +535,8 @@ func downloadProtoc(cachedir string) string {
|
||||||
|
|
||||||
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
fileName := fmt.Sprintf("protoc-%s-%s", version, baseName)
|
||||||
archiveFileName := fileName + ".zip"
|
archiveFileName := fileName + ".zip"
|
||||||
url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName)
|
|
||||||
archivePath := filepath.Join(cachedir, archiveFileName)
|
archivePath := filepath.Join(cachedir, archiveFileName)
|
||||||
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
if err := csdb.DownloadFile(url, archivePath); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
extractDest := filepath.Join(cachedir, fileName)
|
extractDest := filepath.Join(cachedir, fileName)
|
||||||
|
|
@ -826,18 +816,17 @@ func doDebianSource(cmdline []string) {
|
||||||
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used
|
||||||
// to bootstrap the builder Go.
|
// to bootstrap the builder Go.
|
||||||
func downloadGoBootstrapSources(cachedir string) []string {
|
func downloadGoBootstrapSources(cachedir string) []string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
|
|
||||||
var bundles []string
|
var bundles []string
|
||||||
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
for _, booter := range []string{"ppa-builder-1.19", "ppa-builder-1.21", "ppa-builder-1.23"} {
|
||||||
gobootVersion, err := build.Version(csdb, booter)
|
gobootVersion, err := csdb.FindVersion(booter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion)
|
||||||
url := "https://dl.google.com/go/" + file
|
|
||||||
dst := filepath.Join(cachedir, file)
|
dst := filepath.Join(cachedir, file)
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
bundles = append(bundles, dst)
|
bundles = append(bundles, dst)
|
||||||
|
|
@ -847,15 +836,14 @@ func downloadGoBootstrapSources(cachedir string) []string {
|
||||||
|
|
||||||
// downloadGoSources downloads the Go source tarball.
|
// downloadGoSources downloads the Go source tarball.
|
||||||
func downloadGoSources(cachedir string) string {
|
func downloadGoSources(cachedir string) string {
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
dlgoVersion, err := build.Version(csdb, "golang")
|
dlgoVersion, err := csdb.FindVersion("golang")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
file := fmt.Sprintf("go%s.src.tar.gz", dlgoVersion)
|
||||||
url := "https://dl.google.com/go/" + file
|
|
||||||
dst := filepath.Join(cachedir, file)
|
dst := filepath.Join(cachedir, file)
|
||||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
if err := csdb.DownloadFileFromKnownURL(dst); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
|
@ -1181,5 +1169,6 @@ func doPurge(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSanityCheck() {
|
func doSanityCheck() {
|
||||||
build.DownloadAndVerifyChecksums(build.MustLoadChecksums("build/checksums.txt"))
|
csdb := download.MustLoadChecksums("build/checksums.txt")
|
||||||
|
csdb.DownloadAndVerifyAll()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,9 @@ func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn.caps = []p2p.Cap{
|
conn.caps = []p2p.Cap{
|
||||||
{Name: "eth", Version: 67},
|
{Name: "eth", Version: 69},
|
||||||
{Name: "eth", Version: 68},
|
|
||||||
}
|
}
|
||||||
conn.ourHighestProtoVersion = 68
|
conn.ourHighestProtoVersion = 69
|
||||||
return &conn, nil
|
return &conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,7 +155,7 @@ func (c *Conn) ReadEth() (any, error) {
|
||||||
var msg any
|
var msg any
|
||||||
switch int(code) {
|
switch int(code) {
|
||||||
case eth.StatusMsg:
|
case eth.StatusMsg:
|
||||||
msg = new(eth.StatusPacket)
|
msg = new(eth.StatusPacket69)
|
||||||
case eth.GetBlockHeadersMsg:
|
case eth.GetBlockHeadersMsg:
|
||||||
msg = new(eth.GetBlockHeadersPacket)
|
msg = new(eth.GetBlockHeadersPacket)
|
||||||
case eth.BlockHeadersMsg:
|
case eth.BlockHeadersMsg:
|
||||||
|
|
@ -231,7 +230,7 @@ func (c *Conn) ReadSnap() (any, error) {
|
||||||
|
|
||||||
// peer performs both the protocol handshake and the status message
|
// peer performs both the protocol handshake and the status message
|
||||||
// exchange with the node in order to peer with it.
|
// exchange with the node in order to peer with it.
|
||||||
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error {
|
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error {
|
||||||
if err := c.handshake(); err != nil {
|
if err := c.handshake(); err != nil {
|
||||||
return fmt.Errorf("handshake failed: %v", err)
|
return fmt.Errorf("handshake failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +303,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// statusExchange performs a `Status` message exchange with the given node.
|
// statusExchange performs a `Status` message exchange with the given node.
|
||||||
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error {
|
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket69) error {
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
code, data, err := c.Read()
|
code, data, err := c.Read()
|
||||||
|
|
@ -313,12 +312,16 @@ loop:
|
||||||
}
|
}
|
||||||
switch code {
|
switch code {
|
||||||
case eth.StatusMsg + protoOffset(ethProto):
|
case eth.StatusMsg + protoOffset(ethProto):
|
||||||
msg := new(eth.StatusPacket)
|
msg := new(eth.StatusPacket69)
|
||||||
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
||||||
return fmt.Errorf("error decoding status packet: %w", err)
|
return fmt.Errorf("error decoding status packet: %w", err)
|
||||||
}
|
}
|
||||||
if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want {
|
if have, want := msg.LatestBlock, chain.blocks[chain.Len()-1].NumberU64(); have != want {
|
||||||
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
return fmt.Errorf("wrong head block in status, want: %d, have %d",
|
||||||
|
want, have)
|
||||||
|
}
|
||||||
|
if have, want := msg.LatestBlockHash, chain.blocks[chain.Len()-1].Hash(); have != want {
|
||||||
|
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
||||||
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
||||||
}
|
}
|
||||||
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
||||||
|
|
@ -348,13 +351,14 @@ loop:
|
||||||
}
|
}
|
||||||
if status == nil {
|
if status == nil {
|
||||||
// default status message
|
// default status message
|
||||||
status = ð.StatusPacket{
|
status = ð.StatusPacket69{
|
||||||
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
||||||
NetworkID: chain.config.ChainID.Uint64(),
|
NetworkID: chain.config.ChainID.Uint64(),
|
||||||
TD: chain.TD(),
|
|
||||||
Head: chain.blocks[chain.Len()-1].Hash(),
|
|
||||||
Genesis: chain.blocks[0].Hash(),
|
Genesis: chain.blocks[0].Hash(),
|
||||||
ForkID: chain.ForkID(),
|
ForkID: chain.ForkID(),
|
||||||
|
EarliestBlock: 0,
|
||||||
|
LatestBlock: chain.blocks[chain.Len()-1].NumberU64(),
|
||||||
|
LatestBlockHash: chain.blocks[chain.Len()-1].Hash(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.Write(ethProto, eth.StatusMsg, status); err != nil {
|
if err := c.Write(ethProto, eth.StatusMsg, status); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const (
|
||||||
// Unexported devp2p protocol lengths from p2p package.
|
// Unexported devp2p protocol lengths from p2p package.
|
||||||
const (
|
const (
|
||||||
baseProtoLen = 16
|
baseProtoLen = 16
|
||||||
ethProtoLen = 17
|
ethProtoLen = 18
|
||||||
snapProtoLen = 8
|
snapProtoLen = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,13 @@ func (s *Suite) EthTests() []utesting.Test {
|
||||||
{Name: "Status", Fn: s.TestStatus},
|
{Name: "Status", Fn: s.TestStatus},
|
||||||
// get block headers
|
// get block headers
|
||||||
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||||
|
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
|
||||||
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||||
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
||||||
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
||||||
// get block bodies
|
// get history
|
||||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||||
|
{Name: "GetReceipts", Fn: s.TestGetReceipts},
|
||||||
// // malicious handshakes + status
|
// // malicious handshakes + status
|
||||||
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||||
// test transactions
|
// test transactions
|
||||||
|
|
@ -158,6 +160,48 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
|
||||||
|
t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
|
||||||
|
to check if the node disconnects after receiving multiple invalid requests.`)
|
||||||
|
|
||||||
|
conn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dial failed: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if err := conn.peer(s.chain, nil); err != nil {
|
||||||
|
t.Fatalf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request with max uint64 value for a nonexistent block
|
||||||
|
badReq := ð.GetBlockHeadersPacket{
|
||||||
|
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||||
|
Origin: eth.HashOrNumber{Number: ^uint64(0)},
|
||||||
|
Amount: 1,
|
||||||
|
Skip: 0,
|
||||||
|
Reverse: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request 10 times. Some clients are lient on the first few invalids.
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
badReq.RequestId = uint64(i)
|
||||||
|
if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil {
|
||||||
|
if err == errDisc {
|
||||||
|
t.Fatalf("peer disconnected after %d requests", i+1)
|
||||||
|
}
|
||||||
|
t.Fatalf("write failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if peer disconnects at the end.
|
||||||
|
code, _, err := conn.Read()
|
||||||
|
if err == errDisc || code == discMsg {
|
||||||
|
t.Fatal("peer improperly disconnected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
||||||
t.Log(`This test requests blocks headers from the node, performing two requests
|
t.Log(`This test requests blocks headers from the node, performing two requests
|
||||||
concurrently, with different request IDs.`)
|
concurrently, with different request IDs.`)
|
||||||
|
|
@ -375,6 +419,51 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestGetReceipts(t *utesting.T) {
|
||||||
|
t.Log(`This test sends GetReceipts requests to the node for known blocks in the test chain.`)
|
||||||
|
|
||||||
|
conn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dial failed: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
if err := conn.peer(s.chain, nil); err != nil {
|
||||||
|
t.Fatalf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find some blocks containing receipts.
|
||||||
|
var hashes = make([]common.Hash, 0, 3)
|
||||||
|
for i := range s.chain.Len() {
|
||||||
|
block := s.chain.GetBlock(i)
|
||||||
|
if len(block.Transactions()) > 0 {
|
||||||
|
hashes = append(hashes, block.Hash())
|
||||||
|
}
|
||||||
|
if len(hashes) == cap(hashes) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create block bodies request.
|
||||||
|
req := ð.GetReceiptsPacket{
|
||||||
|
RequestId: 66,
|
||||||
|
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
|
||||||
|
}
|
||||||
|
if err := conn.Write(ethProto, eth.GetReceiptsMsg, req); err != nil {
|
||||||
|
t.Fatalf("could not write to connection: %v", err)
|
||||||
|
}
|
||||||
|
// Wait for response.
|
||||||
|
resp := new(eth.ReceiptsPacket[*eth.ReceiptList69])
|
||||||
|
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||||
|
t.Fatalf("error reading block bodies msg: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||||
|
t.Fatalf("unexpected request id in respond", got, want)
|
||||||
|
}
|
||||||
|
if len(resp.List) != len(req.GetReceiptsRequest) {
|
||||||
|
t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// randBuf makes a random buffer size kilobytes large.
|
// randBuf makes a random buffer size kilobytes large.
|
||||||
func randBuf(size int) []byte {
|
func randBuf(size int) []byte {
|
||||||
buf := make([]byte, size*1024)
|
buf := make([]byte, size*1024)
|
||||||
|
|
@ -457,6 +546,31 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestInvalidBlockRangeUpdate(t *utesting.T) {
|
||||||
|
t.Log(`This test sends an invalid BlockRangeUpdate message to the node and expects to be disconnected.`)
|
||||||
|
|
||||||
|
conn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dial failed: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
if err := conn.peer(s.chain, nil); err != nil {
|
||||||
|
t.Fatalf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write(ethProto, eth.BlockRangeUpdateMsg, ð.BlockRangeUpdatePacket{
|
||||||
|
EarliestBlock: 10,
|
||||||
|
LatestBlock: 8,
|
||||||
|
LatestBlockHash: s.chain.GetBlock(8).Hash(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if code, _, err := conn.Read(); err != nil {
|
||||||
|
t.Fatalf("expected disconnect, got err: %v", err)
|
||||||
|
} else if code != discMsg {
|
||||||
|
t.Fatalf("expected disconnect message, got msg code %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestTransaction(t *utesting.T) {
|
func (s *Suite) TestTransaction(t *utesting.T) {
|
||||||
t.Log(`This test sends a valid transaction to the node and checks if the
|
t.Log(`This test sends a valid transaction to the node and checks if the
|
||||||
transaction gets propagated.`)
|
transaction gets propagated.`)
|
||||||
|
|
|
||||||
|
|
@ -363,9 +363,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
core.ProcessWithdrawalQueue(&requests, evm)
|
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err))
|
||||||
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
core.ProcessConsolidationQueue(&requests, evm)
|
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit block
|
// Commit block
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -39,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/internal/era"
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era/eradl"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -190,7 +194,7 @@ This command dumps out the state for a given block (or latest, if none provided)
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
pruneCommand = &cli.Command{
|
pruneHistoryCommand = &cli.Command{
|
||||||
Action: pruneHistory,
|
Action: pruneHistory,
|
||||||
Name: "prune-history",
|
Name: "prune-history",
|
||||||
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
||||||
|
|
@ -201,6 +205,42 @@ The prune-history command removes historical block bodies and receipts from the
|
||||||
blockchain database up to the merge block, while preserving block headers. This
|
blockchain database up to the merge block, while preserving block headers. This
|
||||||
helps reduce storage requirements for nodes that don't need full historical data.`,
|
helps reduce storage requirements for nodes that don't need full historical data.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadEraCommand = &cli.Command{
|
||||||
|
Action: downloadEra,
|
||||||
|
Name: "download-era",
|
||||||
|
Usage: "Fetches era1 files (pre-merge history) from an HTTP endpoint",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Flags: slices.Concat(
|
||||||
|
utils.DatabaseFlags,
|
||||||
|
utils.NetworkFlags,
|
||||||
|
[]cli.Flag{
|
||||||
|
eraBlockFlag,
|
||||||
|
eraEpochFlag,
|
||||||
|
eraAllFlag,
|
||||||
|
eraServerFlag,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
eraBlockFlag = &cli.StringFlag{
|
||||||
|
Name: "block",
|
||||||
|
Usage: "Block number to fetch. (can also be a range <start>-<end>)",
|
||||||
|
}
|
||||||
|
eraEpochFlag = &cli.StringFlag{
|
||||||
|
Name: "epoch",
|
||||||
|
Usage: "Epoch number to fetch (can also be a range <start>-<end>)",
|
||||||
|
}
|
||||||
|
eraAllFlag = &cli.BoolFlag{
|
||||||
|
Name: "all",
|
||||||
|
Usage: "Download all available era1 files",
|
||||||
|
}
|
||||||
|
eraServerFlag = &cli.StringFlag{
|
||||||
|
Name: "server",
|
||||||
|
Usage: "era1 server URL",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||||
|
|
@ -246,10 +286,13 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
_, hash, _, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||||
}
|
}
|
||||||
|
if compatErr != nil {
|
||||||
|
utils.Fatalf("Failed to write chain config: %v", compatErr)
|
||||||
|
}
|
||||||
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -662,3 +705,83 @@ func pruneHistory(ctx *cli.Context) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// downladEra is the era1 file downloader tool.
|
||||||
|
func downloadEra(ctx *cli.Context) error {
|
||||||
|
flags.CheckExclusive(ctx, eraBlockFlag, eraEpochFlag, eraAllFlag)
|
||||||
|
|
||||||
|
// Resolve the network.
|
||||||
|
var network = "mainnet"
|
||||||
|
if utils.IsNetworkPreset(ctx) {
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(utils.MainnetFlag.Name):
|
||||||
|
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||||
|
network = "sepolia"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported network, no known era1 checksums")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the destination directory.
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
ancients := stack.ResolveAncient("chaindata", "")
|
||||||
|
dir := filepath.Join(ancients, "era")
|
||||||
|
|
||||||
|
baseURL := ctx.String(eraServerFlag.Name)
|
||||||
|
if baseURL == "" {
|
||||||
|
return fmt.Errorf("need --%s flag to download", eraServerFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := eradl.New(baseURL, network)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(eraAllFlag.Name):
|
||||||
|
return l.DownloadAll(dir)
|
||||||
|
|
||||||
|
case ctx.IsSet(eraBlockFlag.Name):
|
||||||
|
s := ctx.String(eraBlockFlag.Name)
|
||||||
|
start, end, ok := parseRange(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid block range: %q", s)
|
||||||
|
}
|
||||||
|
return l.DownloadBlockRange(start, end, dir)
|
||||||
|
|
||||||
|
case ctx.IsSet(eraEpochFlag.Name):
|
||||||
|
s := ctx.String(eraEpochFlag.Name)
|
||||||
|
start, end, ok := parseRange(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid epoch range: %q", s)
|
||||||
|
}
|
||||||
|
return l.DownloadEpochRange(start, end, dir)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("specify one of --%s, --%s, or --%s to download", eraAllFlag.Name, eraBlockFlag.Name, eraEpochFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRange(s string) (start uint64, end uint64, ok bool) {
|
||||||
|
if m, _ := regexp.MatchString("[0-9]+", s); m {
|
||||||
|
start, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
end = start
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
if m, _ := regexp.MatchString("[0-9]+-[0-9]+", s); m {
|
||||||
|
s1, s2, _ := strings.Cut(s, "-")
|
||||||
|
start, err := strconv.ParseUint(s1, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
end, err = strconv.ParseUint(s2, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
return start, end, true
|
||||||
|
}
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,8 @@ const (
|
||||||
// child g gets a temporary data directory.
|
// child g gets a temporary data directory.
|
||||||
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
||||||
// --holesky to make the 'writing genesis to disk' faster (no accounts)
|
// --holesky to make the 'writing genesis to disk' faster (no accounts)
|
||||||
// --networkid=1337 to avoid cache bump
|
|
||||||
// --syncmode=full to avoid allocating fast sync bloom
|
// --syncmode=full to avoid allocating fast sync bloom
|
||||||
allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
allArgs := []string{"--holesky", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||||
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
||||||
"--datadir.minfreedisk", "0"}
|
"--datadir.minfreedisk", "0"}
|
||||||
return runGeth(t, append(allArgs, args...)...)
|
return runGeth(t, append(allArgs, args...)...)
|
||||||
|
|
@ -103,17 +102,17 @@ func TestAttachWelcome(t *testing.T) {
|
||||||
"--http", "--http.port", httpPort,
|
"--http", "--http.port", httpPort,
|
||||||
"--ws", "--ws.port", wsPort)
|
"--ws", "--ws.port", wsPort)
|
||||||
t.Run("ipc", func(t *testing.T) {
|
t.Run("ipc", func(t *testing.T) {
|
||||||
waitForEndpoint(t, ipc, 4*time.Second)
|
waitForEndpoint(t, ipc, 2*time.Minute)
|
||||||
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||||
})
|
})
|
||||||
t.Run("http", func(t *testing.T) {
|
t.Run("http", func(t *testing.T) {
|
||||||
endpoint := "http://127.0.0.1:" + httpPort
|
endpoint := "http://127.0.0.1:" + httpPort
|
||||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
waitForEndpoint(t, endpoint, 2*time.Minute)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
t.Run("ws", func(t *testing.T) {
|
t.Run("ws", func(t *testing.T) {
|
||||||
endpoint := "ws://127.0.0.1:" + wsPort
|
endpoint := "ws://127.0.0.1:" + wsPort
|
||||||
waitForEndpoint(t, endpoint, 4*time.Second)
|
waitForEndpoint(t, endpoint, 2*time.Minute)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
geth.Kill()
|
geth.Kill()
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,7 @@ var (
|
||||||
utils.LogNoHistoryFlag,
|
utils.LogNoHistoryFlag,
|
||||||
utils.LogExportCheckpointsFlag,
|
utils.LogExportCheckpointsFlag,
|
||||||
utils.StateHistoryFlag,
|
utils.StateHistoryFlag,
|
||||||
utils.LightServeFlag, // deprecated
|
|
||||||
utils.LightIngressFlag, // deprecated
|
|
||||||
utils.LightEgressFlag, // deprecated
|
|
||||||
utils.LightMaxPeersFlag, // deprecated
|
|
||||||
utils.LightNoPruneFlag, // deprecated
|
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.LightNoSyncServeFlag, // deprecated
|
|
||||||
utils.EthRequiredBlocksFlag,
|
utils.EthRequiredBlocksFlag,
|
||||||
utils.LegacyWhitelistFlag, // deprecated
|
utils.LegacyWhitelistFlag, // deprecated
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
|
|
@ -225,7 +219,8 @@ func init() {
|
||||||
removedbCommand,
|
removedbCommand,
|
||||||
dumpCommand,
|
dumpCommand,
|
||||||
dumpGenesisCommand,
|
dumpGenesisCommand,
|
||||||
pruneCommand,
|
pruneHistoryCommand,
|
||||||
|
downloadEraCommand,
|
||||||
// See accountcmd.go:
|
// See accountcmd.go:
|
||||||
accountCommand,
|
accountCommand,
|
||||||
walletCommand,
|
walletCommand,
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,8 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||||
}
|
}
|
||||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
encReceipts := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
||||||
|
if _, err := chain.InsertReceiptChain([]*types.Block{block}, encReceipts, 2^64-1); err != nil {
|
||||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||||
}
|
}
|
||||||
imported += 1
|
imported += 1
|
||||||
|
|
|
||||||
|
|
@ -1245,28 +1245,6 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setLes shows the deprecation warnings for LES flags.
|
|
||||||
func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
|
|
||||||
if ctx.IsSet(LightServeFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightServeFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightIngressFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightIngressFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightEgressFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightEgressFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightMaxPeersFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightMaxPeersFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightNoPruneFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoPruneFlag.Name)
|
|
||||||
}
|
|
||||||
if ctx.IsSet(LightNoSyncServeFlag.Name) {
|
|
||||||
log.Warn("The light server has been deprecated, please remove this flag", "flag", LightNoSyncServeFlag.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||||
// for Geth and returns half of the allowance to assign to the database.
|
// for Geth and returns half of the allowance to assign to the database.
|
||||||
func MakeDatabaseHandles(max int) int {
|
func MakeDatabaseHandles(max int) int {
|
||||||
|
|
@ -1571,8 +1549,8 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
|
|
||||||
// SetEthConfig applies eth-related command line flags to the config.
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
// Avoid conflicting network flags
|
// Avoid conflicting network flags, don't allow network id override on preset networks
|
||||||
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag)
|
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag, NetworkIdFlag)
|
||||||
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||||
|
|
||||||
// Set configurations from CLI flags
|
// Set configurations from CLI flags
|
||||||
|
|
@ -1582,7 +1560,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
setBlobPool(ctx, &cfg.BlobPool)
|
setBlobPool(ctx, &cfg.BlobPool)
|
||||||
setMiner(ctx, &cfg.Miner)
|
setMiner(ctx, &cfg.Miner)
|
||||||
setRequiredBlocks(ctx, cfg)
|
setRequiredBlocks(ctx, cfg)
|
||||||
setLes(ctx, cfg)
|
|
||||||
|
|
||||||
// Cap the cache allowance and tune the garbage collector
|
// Cap the cache allowance and tune the garbage collector
|
||||||
mem, err := gopsutil.VirtualMemory()
|
mem, err := gopsutil.VirtualMemory()
|
||||||
|
|
@ -1712,7 +1689,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
if ctx.IsSet(VMEnableDebugFlag.Name) {
|
||||||
// TODO(fjl): force-enable this in --dev mode
|
|
||||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1743,34 +1719,25 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
// Override any default configs for hard coded networks.
|
// Override any default configs for hard coded networks.
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(MainnetFlag.Name):
|
case ctx.Bool(MainnetFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
cfg.NetworkId = 1
|
||||||
cfg.NetworkId = 1
|
|
||||||
}
|
|
||||||
cfg.Genesis = core.DefaultGenesisBlock()
|
cfg.Genesis = core.DefaultGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
|
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
|
||||||
case ctx.Bool(HoleskyFlag.Name):
|
case ctx.Bool(HoleskyFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
cfg.NetworkId = 17000
|
||||||
cfg.NetworkId = 17000
|
|
||||||
}
|
|
||||||
cfg.Genesis = core.DefaultHoleskyGenesisBlock()
|
cfg.Genesis = core.DefaultHoleskyGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.HoleskyGenesisHash)
|
SetDNSDiscoveryDefaults(cfg, params.HoleskyGenesisHash)
|
||||||
case ctx.Bool(SepoliaFlag.Name):
|
case ctx.Bool(SepoliaFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
cfg.NetworkId = 11155111
|
||||||
cfg.NetworkId = 11155111
|
|
||||||
}
|
|
||||||
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
||||||
case ctx.Bool(HoodiFlag.Name):
|
case ctx.Bool(HoodiFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
cfg.NetworkId = 560048
|
||||||
cfg.NetworkId = 560048
|
|
||||||
}
|
|
||||||
cfg.Genesis = core.DefaultHoodiGenesisBlock()
|
cfg.Genesis = core.DefaultHoodiGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash)
|
SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash)
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
cfg.NetworkId = 1337
|
||||||
cfg.NetworkId = 1337
|
|
||||||
}
|
|
||||||
cfg.SyncMode = ethconfig.FullSync
|
cfg.SyncMode = ethconfig.FullSync
|
||||||
|
cfg.EnablePreimageRecording = true
|
||||||
// Create new developer account or reuse existing one
|
// Create new developer account or reuse existing one
|
||||||
var (
|
var (
|
||||||
developer accounts.Account
|
developer accounts.Account
|
||||||
|
|
@ -1854,10 +1821,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" {
|
if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" {
|
||||||
Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
|
Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
|
||||||
}
|
}
|
||||||
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
// The initialization of the KZG library can take up to 2 seconds
|
||||||
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
// We start this here in parallel, so it should be available
|
||||||
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
// once we start executing blocks. It's threadsafe.
|
||||||
}
|
go func() {
|
||||||
|
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
||||||
|
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
||||||
|
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// VM tracing config.
|
// VM tracing config.
|
||||||
if ctx.IsSet(VMTraceFlag.Name) {
|
if ctx.IsSet(VMTraceFlag.Name) {
|
||||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,6 @@ var DeprecatedFlags = []cli.Flag{
|
||||||
CacheTrieRejournalFlag,
|
CacheTrieRejournalFlag,
|
||||||
LegacyDiscoveryV5Flag,
|
LegacyDiscoveryV5Flag,
|
||||||
TxLookupLimitFlag,
|
TxLookupLimitFlag,
|
||||||
LightServeFlag,
|
|
||||||
LightIngressFlag,
|
|
||||||
LightEgressFlag,
|
|
||||||
LightMaxPeersFlag,
|
|
||||||
LightNoPruneFlag,
|
|
||||||
LightNoSyncServeFlag,
|
|
||||||
LogBacktraceAtFlag,
|
LogBacktraceAtFlag,
|
||||||
LogDebugFlag,
|
LogDebugFlag,
|
||||||
MinerNewPayloadTimeoutFlag,
|
MinerNewPayloadTimeoutFlag,
|
||||||
|
|
@ -88,37 +82,6 @@ var (
|
||||||
Value: ethconfig.Defaults.TransactionHistory,
|
Value: ethconfig.Defaults.TransactionHistory,
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
// Light server and client settings, Deprecated November 2023
|
|
||||||
LightServeFlag = &cli.IntFlag{
|
|
||||||
Name: "light.serve",
|
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightIngressFlag = &cli.IntFlag{
|
|
||||||
Name: "light.ingress",
|
|
||||||
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightEgressFlag = &cli.IntFlag{
|
|
||||||
Name: "light.egress",
|
|
||||||
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightMaxPeersFlag = &cli.IntFlag{
|
|
||||||
Name: "light.maxpeers",
|
|
||||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightNoPruneFlag = &cli.BoolFlag{
|
|
||||||
Name: "light.nopruning",
|
|
||||||
Usage: "Disable ancient light chain data pruning (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
LightNoSyncServeFlag = &cli.BoolFlag{
|
|
||||||
Name: "light.nosyncserve",
|
|
||||||
Usage: "Enables serving light clients before syncing (deprecated)",
|
|
||||||
Category: flags.DeprecatedCategory,
|
|
||||||
}
|
|
||||||
// Deprecated November 2023
|
// Deprecated November 2023
|
||||||
LogBacktraceAtFlag = &cli.StringFlag{
|
LogBacktraceAtFlag = &cli.StringFlag{
|
||||||
Name: "log.backtrace",
|
Name: "log.backtrace",
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
if !disk {
|
if !disk {
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
} else {
|
} else {
|
||||||
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false, true)
|
pdb, err := pebble.New(b.TempDir(), 128, 128, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("cannot create temporary database: %v", err)
|
b.Fatalf("cannot create temporary database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +303,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin
|
||||||
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false, true)
|
pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database: %v", err)
|
b.Fatalf("error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +316,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
func benchReadChain(b *testing.B, full bool, count uint64) {
|
func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
dir := b.TempDir()
|
dir := b.TempDir()
|
||||||
|
|
||||||
pdb, err := pebble.New(dir, 1024, 128, "", false, true)
|
pdb, err := pebble.New(dir, 1024, 128, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database: %v", err)
|
b.Fatalf("error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -332,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
pdb, err = pebble.New(dir, 1024, 128, "", false, true)
|
pdb, err = pebble.New(dir, 1024, 128, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database: %v", err)
|
b.Fatalf("error opening database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ var (
|
||||||
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -92,8 +93,10 @@ var (
|
||||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||||
|
|
||||||
blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil)
|
blockPrefetchExecuteTimer = metrics.NewRegisteredResettingTimer("chain/prefetch/executes", nil)
|
||||||
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
|
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
|
||||||
|
blockPrefetchTxsInvalidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/invalid", nil)
|
||||||
|
blockPrefetchTxsValidMeter = metrics.NewRegisteredMeter("chain/prefetch/txs/valid", nil)
|
||||||
|
|
||||||
errInsertionInterrupted = errors.New("insertion is interrupted")
|
errInsertionInterrupted = errors.New("insertion is interrupted")
|
||||||
errChainStopped = errors.New("blockchain is stopped")
|
errChainStopped = errors.New("blockchain is stopped")
|
||||||
|
|
@ -180,8 +183,13 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
||||||
}
|
}
|
||||||
if c.StateScheme == rawdb.PathScheme {
|
if c.StateScheme == rawdb.PathScheme {
|
||||||
config.PathDB = &pathdb.Config{
|
config.PathDB = &pathdb.Config{
|
||||||
StateHistory: c.StateHistory,
|
StateHistory: c.StateHistory,
|
||||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
TrieCleanSize: c.TrieCleanLimit * 1024 * 1024,
|
||||||
|
StateCleanSize: c.SnapshotLimit * 1024 * 1024,
|
||||||
|
|
||||||
|
// TODO(rjl493456442): The write buffer represents the memory limit used
|
||||||
|
// for flushing both trie data and state data to disk. The config name
|
||||||
|
// should be updated to eliminate the confusion.
|
||||||
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
|
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -377,11 +385,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
// Do nothing here until the state syncer picks it up.
|
// Do nothing here until the state syncer picks it up.
|
||||||
log.Info("Genesis state is missing, wait state sync")
|
log.Info("Genesis state is missing, wait state sync")
|
||||||
} else {
|
} else {
|
||||||
// Head state is missing, before the state recovery, find out the
|
// Head state is missing, before the state recovery, find out the disk
|
||||||
// disk layer point of snapshot(if it's enabled). Make sure the
|
// layer point of snapshot(if it's enabled). Make sure the rewound point
|
||||||
// rewound point is lower than disk layer.
|
// is lower than disk layer.
|
||||||
|
//
|
||||||
|
// Note it's unnecessary in path mode which always keep trie data and
|
||||||
|
// state data consistent.
|
||||||
var diskRoot common.Hash
|
var diskRoot common.Hash
|
||||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
if bc.cacheConfig.SnapshotLimit > 0 && bc.cacheConfig.StateScheme == rawdb.HashScheme {
|
||||||
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
|
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
|
||||||
}
|
}
|
||||||
if diskRoot != (common.Hash{}) {
|
if diskRoot != (common.Hash{}) {
|
||||||
|
|
@ -454,7 +465,32 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bc.setupSnapshot()
|
||||||
|
|
||||||
|
// Rewind the chain in case of an incompatible config upgrade.
|
||||||
|
if compatErr != nil {
|
||||||
|
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
|
||||||
|
if compatErr.RewindToTime > 0 {
|
||||||
|
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
|
||||||
|
} else {
|
||||||
|
bc.SetHead(compatErr.RewindToBlock)
|
||||||
|
}
|
||||||
|
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start tx indexer if it's enabled.
|
||||||
|
if txLookupLimit != nil {
|
||||||
|
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
||||||
|
}
|
||||||
|
return bc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *BlockChain) setupSnapshot() {
|
||||||
|
// Short circuit if the chain is established with path scheme, as the
|
||||||
|
// state snapshot has been integrated into path database natively.
|
||||||
|
if bc.cacheConfig.StateScheme == rawdb.PathScheme {
|
||||||
|
return
|
||||||
|
}
|
||||||
// Load any existing snapshot, regenerating it if loading failed
|
// Load any existing snapshot, regenerating it if loading failed
|
||||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||||
// If the chain was rewound past the snapshot persistent layer (causing
|
// If the chain was rewound past the snapshot persistent layer (causing
|
||||||
|
|
@ -462,7 +498,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
// in recovery mode and in that case, don't invalidate the snapshot on a
|
// in recovery mode and in that case, don't invalidate the snapshot on a
|
||||||
// head mismatch.
|
// head mismatch.
|
||||||
var recover bool
|
var recover bool
|
||||||
|
|
||||||
head := bc.CurrentBlock()
|
head := bc.CurrentBlock()
|
||||||
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
|
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
|
||||||
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
|
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
|
||||||
|
|
@ -479,22 +514,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
// Re-initialize the state database with snapshot
|
// Re-initialize the state database with snapshot
|
||||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewind the chain in case of an incompatible config upgrade.
|
|
||||||
if compatErr != nil {
|
|
||||||
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
|
|
||||||
if compatErr.RewindToTime > 0 {
|
|
||||||
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
|
|
||||||
} else {
|
|
||||||
bc.SetHead(compatErr.RewindToBlock)
|
|
||||||
}
|
|
||||||
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
|
||||||
}
|
|
||||||
// Start tx indexer if it's enabled.
|
|
||||||
if txLookupLimit != nil {
|
|
||||||
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
|
||||||
}
|
|
||||||
return bc, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty returns an indicator whether the blockchain is empty.
|
// empty returns an indicator whether the blockchain is empty.
|
||||||
|
|
@ -979,17 +998,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||||
// Ignore the error here since light client won't hit this path
|
// Ignore the error here since light client won't hit this path
|
||||||
frozen, _ := bc.db.Ancients()
|
frozen, _ := bc.db.Ancients()
|
||||||
if num+1 <= frozen {
|
if num+1 <= frozen {
|
||||||
// Truncate all relative data(header, total difficulty, body, receipt
|
// The chain segment, such as the block header, canonical hash,
|
||||||
// and canonical hash) from ancient store.
|
// body, and receipt, will be removed from the ancient store
|
||||||
if _, err := bc.db.TruncateHead(num); err != nil {
|
// in one go.
|
||||||
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
|
//
|
||||||
}
|
// The hash-to-number mapping in the key-value store will be
|
||||||
// Remove the hash <-> number mapping from the active store.
|
// removed by the hc.SetHead function.
|
||||||
rawdb.DeleteHeaderNumber(db, hash)
|
|
||||||
} else {
|
} else {
|
||||||
// Remove relative body and receipts from the active store.
|
// Remove the associated body and receipts from the key-value store.
|
||||||
// The header, total difficulty and canonical hash will be
|
// The header, hash-to-number mapping, and canonical hash will be
|
||||||
// removed in the hc.SetHead function.
|
// removed by the hc.SetHead function.
|
||||||
rawdb.DeleteBody(db, hash, num)
|
rawdb.DeleteBody(db, hash, num)
|
||||||
rawdb.DeleteReceipts(db, hash, num)
|
rawdb.DeleteReceipts(db, hash, num)
|
||||||
}
|
}
|
||||||
|
|
@ -1291,12 +1309,11 @@ const (
|
||||||
//
|
//
|
||||||
// The optional ancientLimit can also be specified and chain segment before that
|
// The optional ancientLimit can also be specified and chain segment before that
|
||||||
// will be directly stored in the ancient, getting rid of the chain migration.
|
// will be directly stored in the ancient, getting rid of the chain migration.
|
||||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts, ancientLimit uint64) (int, error) {
|
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []rlp.RawValue, ancientLimit uint64) (int, error) {
|
||||||
// Verify the supplied headers before insertion without lock
|
// Verify the supplied headers before insertion without lock
|
||||||
var headers []*types.Header
|
var headers []*types.Header
|
||||||
for _, block := range blockChain {
|
for _, block := range blockChain {
|
||||||
headers = append(headers, block.Header())
|
headers = append(headers, block.Header())
|
||||||
|
|
||||||
// Here we also validate that blob transactions in the block do not
|
// Here we also validate that blob transactions in the block do not
|
||||||
// contain a sidecar. While the sidecar does not affect the block hash
|
// contain a sidecar. While the sidecar does not affect the block hash
|
||||||
// or tx hash, sending blobs within a block is not allowed.
|
// or tx hash, sending blobs within a block is not allowed.
|
||||||
|
|
@ -1339,11 +1356,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
//
|
//
|
||||||
// this function only accepts canonical chain data. All side chain will be reverted
|
// this function only accepts canonical chain data. All side chain will be reverted
|
||||||
// eventually.
|
// eventually.
|
||||||
writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeAncient := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) {
|
||||||
// Ensure genesis is in the ancient store
|
// Ensure genesis is in the ancient store
|
||||||
if blockChain[0].NumberU64() == 1 {
|
if blockChain[0].NumberU64() == 1 {
|
||||||
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error writing genesis to ancients", "err", err)
|
log.Error("Error writing genesis to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -1361,7 +1378,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
size += writeSize
|
size += writeSize
|
||||||
|
|
||||||
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
|
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
|
||||||
if err := bc.db.Sync(); err != nil {
|
if err := bc.db.SyncAncient(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// Write hash to number mappings
|
// Write hash to number mappings
|
||||||
|
|
@ -1386,7 +1403,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// existing local chain segments (reorg around the chain tip). The reorganized part
|
// existing local chain segments (reorg around the chain tip). The reorganized part
|
||||||
// will be included in the provided chain segment, and stale canonical markers will be
|
// will be included in the provided chain segment, and stale canonical markers will be
|
||||||
// silently rewritten. Therefore, no explicit reorg logic is needed.
|
// silently rewritten. Therefore, no explicit reorg logic is needed.
|
||||||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeLive := func(blockChain types.Blocks, receiptChain []rlp.RawValue) (int, error) {
|
||||||
var (
|
var (
|
||||||
skipPresenceCheck = false
|
skipPresenceCheck = false
|
||||||
batch = bc.db.NewBatch()
|
batch = bc.db.NewBatch()
|
||||||
|
|
@ -1411,7 +1428,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// Write all the data out into the database
|
// Write all the data out into the database
|
||||||
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||||
rawdb.WriteBlock(batch, block)
|
rawdb.WriteBlock(batch, block)
|
||||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
rawdb.WriteRawReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||||
|
|
||||||
// Write everything belongs to the blocks into the database. So that
|
// Write everything belongs to the blocks into the database. So that
|
||||||
// we can ensure all components of body is completed(body, receipts)
|
// we can ensure all components of body is completed(body, receipts)
|
||||||
|
|
@ -1759,18 +1776,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
bc.reportBlock(block, nil, err)
|
bc.reportBlock(block, nil, err)
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
// No validation errors for the first block (or chain prefix skipped)
|
|
||||||
var activeState *state.StateDB
|
|
||||||
defer func() {
|
|
||||||
// The chain importer is starting and stopping trie prefetchers. If a bad
|
|
||||||
// block or other error is hit however, an early return may not properly
|
|
||||||
// terminate the background threads. This defer ensures that we clean up
|
|
||||||
// and dangling prefetcher, without deferring each and holding on live refs.
|
|
||||||
if activeState != nil {
|
|
||||||
activeState.StopPrefetcher()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Track the singleton witness from this chain insertion (if any)
|
// Track the singleton witness from this chain insertion (if any)
|
||||||
var witness *stateless.Witness
|
var witness *stateless.Witness
|
||||||
|
|
||||||
|
|
@ -1826,63 +1831,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Retrieve the parent block and it's state to execute on top
|
// Retrieve the parent block and it's state to execute on top
|
||||||
start := time.Now()
|
|
||||||
parent := it.previous()
|
parent := it.previous()
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
}
|
}
|
||||||
statedb, err := state.New(parent.Root, bc.statedb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, it.index, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
|
||||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
|
||||||
// Generate witnesses either if we're self-testing, or if it's the
|
|
||||||
// only block being inserted. A bit crude, but witnesses are huge,
|
|
||||||
// so we refuse to make an entire chain of them.
|
|
||||||
if bc.vmConfig.StatelessSelfValidation || (makeWitness && len(chain) == 1) {
|
|
||||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, it.index, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statedb.StartPrefetcher("chain", witness)
|
|
||||||
}
|
|
||||||
activeState = statedb
|
|
||||||
|
|
||||||
// If we have a followup block, run that against the current state to pre-cache
|
|
||||||
// transactions and probabilistically some of the account/storage trie nodes.
|
|
||||||
var followupInterrupt atomic.Bool
|
|
||||||
if !bc.cacheConfig.TrieCleanNoPrefetch {
|
|
||||||
if followup, err := it.peek(); followup != nil && err == nil {
|
|
||||||
throwaway, _ := state.New(parent.Root, bc.statedb)
|
|
||||||
|
|
||||||
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
|
|
||||||
// Disable tracing for prefetcher executions.
|
|
||||||
vmCfg := bc.vmConfig
|
|
||||||
vmCfg.Tracer = nil
|
|
||||||
bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt)
|
|
||||||
|
|
||||||
blockPrefetchExecuteTimer.Update(time.Since(start))
|
|
||||||
if followupInterrupt.Load() {
|
|
||||||
blockPrefetchInterruptMeter.Mark(1)
|
|
||||||
}
|
|
||||||
}(time.Now(), followup, throwaway)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The traced section of block import.
|
// The traced section of block import.
|
||||||
res, err := bc.processBlock(block, statedb, start, setHead)
|
start := time.Now()
|
||||||
followupInterrupt.Store(true)
|
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
// Report the import stats before returning the various results
|
// Report the import stats before returning the various results
|
||||||
stats.processed++
|
stats.processed++
|
||||||
stats.usedGas += res.usedGas
|
stats.usedGas += res.usedGas
|
||||||
|
witness = res.witness
|
||||||
|
|
||||||
var snapDiffItems, snapBufItems common.StorageSize
|
var snapDiffItems, snapBufItems common.StorageSize
|
||||||
if bc.snaps != nil {
|
if bc.snaps != nil {
|
||||||
|
|
@ -1938,11 +1900,74 @@ type blockProcessingResult struct {
|
||||||
usedGas uint64
|
usedGas uint64
|
||||||
procTime time.Duration
|
procTime time.Duration
|
||||||
status WriteStatus
|
status WriteStatus
|
||||||
|
witness *stateless.Witness
|
||||||
}
|
}
|
||||||
|
|
||||||
// processBlock executes and validates the given block. If there was no error
|
// processBlock executes and validates the given block. If there was no error
|
||||||
// it writes the block and associated state to database.
|
// it writes the block and associated state to database.
|
||||||
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) {
|
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
startTime = time.Now()
|
||||||
|
statedb *state.StateDB
|
||||||
|
interrupt atomic.Bool
|
||||||
|
)
|
||||||
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
|
||||||
|
if bc.cacheConfig.TrieCleanNoPrefetch {
|
||||||
|
statedb, err = state.New(parentRoot, bc.statedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If prefetching is enabled, run that against the current state to pre-cache
|
||||||
|
// transactions and probabilistically some of the account/storage trie nodes.
|
||||||
|
//
|
||||||
|
// Note: the main processor and prefetcher share the same reader with a local
|
||||||
|
// cache for mitigating the overhead of state access.
|
||||||
|
reader, err := bc.statedb.ReaderWithCache(parentRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
throwaway, err := state.NewWithReader(parentRoot, bc.statedb, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statedb, err = state.NewWithReader(parentRoot, bc.statedb, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
|
||||||
|
// Disable tracing for prefetcher executions.
|
||||||
|
vmCfg := bc.vmConfig
|
||||||
|
vmCfg.Tracer = nil
|
||||||
|
bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt)
|
||||||
|
|
||||||
|
blockPrefetchExecuteTimer.Update(time.Since(start))
|
||||||
|
if interrupt.Load() {
|
||||||
|
blockPrefetchInterruptMeter.Mark(1)
|
||||||
|
}
|
||||||
|
}(time.Now(), throwaway, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
|
// useless due to the intermediate root hashing after each transaction.
|
||||||
|
var witness *stateless.Witness
|
||||||
|
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||||
|
// Generate witnesses either if we're self-testing, or if it's the
|
||||||
|
// only block being inserted. A bit crude, but witnesses are huge,
|
||||||
|
// so we refuse to make an entire chain of them.
|
||||||
|
if bc.vmConfig.StatelessSelfValidation || makeWitness {
|
||||||
|
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statedb.StartPrefetcher("chain", witness)
|
||||||
|
defer statedb.StopPrefetcher()
|
||||||
|
}
|
||||||
|
|
||||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||||
Block: block,
|
Block: block,
|
||||||
|
|
@ -2001,7 +2026,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xvtime := time.Since(xvstart)
|
xvtime := time.Since(xvstart)
|
||||||
proctime := time.Since(start) // processing + validation + cross validation
|
proctime := time.Since(startTime) // processing + validation + cross validation
|
||||||
|
|
||||||
// Update the metrics touched during block processing and validation
|
// Update the metrics touched during block processing and validation
|
||||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||||
|
|
@ -2042,9 +2067,14 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, 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(start)
|
blockInsertTimer.UpdateSince(startTime)
|
||||||
|
|
||||||
return &blockProcessingResult{usedGas: res.GasUsed, procTime: proctime, status: status}, nil
|
return &blockProcessingResult{
|
||||||
|
usedGas: res.GasUsed,
|
||||||
|
procTime: proctime,
|
||||||
|
status: status,
|
||||||
|
witness: witness,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||||
|
|
@ -2528,14 +2558,22 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er
|
||||||
// logForkReadiness will write a log when a future fork is scheduled, but not
|
// logForkReadiness will write a log when a future fork is scheduled, but not
|
||||||
// active. This is useful so operators know their client is ready for the fork.
|
// active. This is useful so operators know their client is ready for the fork.
|
||||||
func (bc *BlockChain) logForkReadiness(block *types.Block) {
|
func (bc *BlockChain) logForkReadiness(block *types.Block) {
|
||||||
c := bc.Config()
|
config := bc.Config()
|
||||||
current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64)
|
current, last := config.LatestFork(block.Time()), config.LatestFork(math.MaxUint64)
|
||||||
t := c.Timestamp(last)
|
|
||||||
if t == nil {
|
// Short circuit if the timestamp of the last fork is undefined,
|
||||||
|
// or if the network has already passed the last configured fork.
|
||||||
|
t := config.Timestamp(last)
|
||||||
|
if t == nil || current >= last {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
at := time.Unix(int64(*t), 0)
|
at := time.Unix(int64(*t), 0)
|
||||||
if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
|
|
||||||
|
// Only log if:
|
||||||
|
// - Current time is before the fork activation time
|
||||||
|
// - Enough time has passed since last alert
|
||||||
|
now := time.Now()
|
||||||
|
if now.Before(at) && now.After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
|
||||||
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
|
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
|
||||||
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
|
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
|
||||||
bc.lastForkReadyAlert = time.Now()
|
bc.lastForkReadyAlert = time.Now()
|
||||||
|
|
@ -2611,7 +2649,7 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
|
||||||
first = headers[0].Number.Uint64()
|
first = headers[0].Number.Uint64()
|
||||||
)
|
)
|
||||||
if first == 1 && frozen == 0 {
|
if first == 1 && frozen == 0 {
|
||||||
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
_, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []rlp.RawValue{rlp.EmptyList})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error writing genesis to ancients", "err", err)
|
log.Error("Error writing genesis to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -2627,7 +2665,8 @@ func (bc *BlockChain) InsertHeadersBeforeCutoff(headers []*types.Header) (int, e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if err := bc.db.Sync(); err != nil {
|
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
|
||||||
|
if err := bc.db.SyncAncient(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// Write hash to number mappings
|
// Write hash to number mappings
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,12 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
// Fetch the timings for the batch
|
// Fetch the timings for the batch
|
||||||
var (
|
var (
|
||||||
now = mclock.Now()
|
now = mclock.Now()
|
||||||
elapsed = now.Sub(st.startTime)
|
elapsed = now.Sub(st.startTime) + 1 // prevent zero division
|
||||||
|
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
|
||||||
|
|
@ -58,7 +62,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||||
context := []interface{}{
|
context := []interface{}{
|
||||||
"number", end.Number(), "hash", end.Hash(),
|
"number", end.Number(), "hash", end.Hash(),
|
||||||
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
||||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
|
"elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps,
|
||||||
}
|
}
|
||||||
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
||||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||||
|
|
@ -138,6 +142,7 @@ func (it *insertIterator) next() (*types.Block, error) {
|
||||||
//
|
//
|
||||||
// Both header and body validation errors (nil too) is cached into the iterator
|
// Both header and body validation errors (nil too) is cached into the iterator
|
||||||
// to avoid duplicating work on the following next() call.
|
// to avoid duplicating work on the following next() call.
|
||||||
|
// nolint:unused
|
||||||
func (it *insertIterator) peek() (*types.Block, error) {
|
func (it *insertIterator) peek() (*types.Block, error) {
|
||||||
// If we reached the end of the chain, abort
|
// If we reached the end of the chain, abort
|
||||||
if it.index+1 >= len(it.chain) {
|
if it.index+1 >= len(it.chain) {
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,24 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return receipts
|
return receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRawReceipts retrieves the receipts for all transactions in a given block
|
||||||
|
// without deriving the internal fields and the Bloom.
|
||||||
|
func (bc *BlockChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts {
|
||||||
|
if receipts, ok := bc.receiptsCache.Get(hash); ok {
|
||||||
|
return receipts
|
||||||
|
}
|
||||||
|
return rawdb.ReadRawReceipts(bc.db, hash, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReceiptsRLP retrieves the receipts of a block.
|
||||||
|
func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue {
|
||||||
|
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||||
|
if number == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return rawdb.ReadReceiptsRLP(bc.db, hash, *number)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
||||||
// a specific distance is reached.
|
// a specific distance is reached.
|
||||||
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
||||||
|
|
@ -262,42 +280,20 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
||||||
// GetTransactionLookup retrieves the lookup along with the transaction
|
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||||
// itself associate with the given transaction hash.
|
// itself associate with the given transaction hash.
|
||||||
//
|
//
|
||||||
// An error will be returned if the transaction is not found, and background
|
// A null will be returned if the transaction is not found. This can be due to
|
||||||
// indexing for transactions is still in progress. The transaction might be
|
// the transaction indexer not being finished. The caller must explicitly check
|
||||||
// reachable shortly once it's indexed.
|
// the indexer progress.
|
||||||
//
|
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
|
||||||
// A null will be returned in the transaction is not found and background
|
|
||||||
// transaction indexing is already finished. The transaction is not existent
|
|
||||||
// from the node's perspective.
|
|
||||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
|
||||||
bc.txLookupLock.RLock()
|
bc.txLookupLock.RLock()
|
||||||
defer bc.txLookupLock.RUnlock()
|
defer bc.txLookupLock.RUnlock()
|
||||||
|
|
||||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||||
return item.lookup, item.transaction, nil
|
return item.lookup, item.transaction
|
||||||
}
|
}
|
||||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
progress, err := bc.TxIndexProgress()
|
return nil, nil
|
||||||
if err != nil {
|
|
||||||
// No error is returned if the transaction indexing progress is unreachable
|
|
||||||
// due to unexpected internal errors. In such cases, it is impossible to
|
|
||||||
// determine whether the transaction does not exist or has simply not been
|
|
||||||
// indexed yet without a progress marker.
|
|
||||||
//
|
|
||||||
// In such scenarios, the transaction is treated as unreachable, though
|
|
||||||
// this is clearly an unintended and unexpected situation.
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
// The transaction indexing is not finished yet, returning an
|
|
||||||
// error to explicitly indicate it.
|
|
||||||
if !progress.Done() {
|
|
||||||
return nil, nil, errors.New("transaction indexing still in progress")
|
|
||||||
}
|
|
||||||
// The transaction is already indexed, the transaction is either
|
|
||||||
// not existent or not in the range of index, returning null.
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
}
|
||||||
lookup := &rawdb.LegacyTxLookupEntry{
|
lookup := &rawdb.LegacyTxLookupEntry{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
|
@ -308,7 +304,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
||||||
lookup: lookup,
|
lookup: lookup,
|
||||||
transaction: tx,
|
transaction: tx,
|
||||||
})
|
})
|
||||||
return lookup, tx, nil
|
return lookup, tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||||
|
func (bc *BlockChain) TxIndexDone() bool {
|
||||||
|
progress, err := bc.TxIndexProgress()
|
||||||
|
if err != nil {
|
||||||
|
// No error is returned if the transaction indexing progress is unreachable
|
||||||
|
// due to unexpected internal errors. In such cases, it is impossible to
|
||||||
|
// determine whether the transaction does not exist or has simply not been
|
||||||
|
// indexed yet without a progress marker.
|
||||||
|
//
|
||||||
|
// In such scenarios, the transaction is treated as unreachable, though
|
||||||
|
// this is clearly an unintended and unexpected situation.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return progress.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasState checks if state trie is fully present in the database or not.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
|
|
@ -404,7 +416,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||||
if bc.txIndexer == nil {
|
if bc.txIndexer == nil {
|
||||||
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
||||||
}
|
}
|
||||||
return bc.txIndexer.txIndexProgress()
|
return bc.txIndexer.txIndexProgress(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistoryPruningCutoff returns the configured history pruning point.
|
// HistoryPruningCutoff returns the configured history pruning point.
|
||||||
|
|
|
||||||
|
|
@ -1765,7 +1765,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1791,7 +1791,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
defer engine.Close()
|
defer engine.Close()
|
||||||
if snapshots {
|
if snapshots && scheme == rawdb.HashScheme {
|
||||||
config.SnapshotLimit = 256
|
config.SnapshotLimit = 256
|
||||||
config.SnapshotWait = true
|
config.SnapshotWait = true
|
||||||
}
|
}
|
||||||
|
|
@ -1820,7 +1820,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
|
if err := chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false); err != nil {
|
||||||
t.Fatalf("Failed to flush trie state: %v", err)
|
t.Fatalf("Failed to flush trie state: %v", err)
|
||||||
}
|
}
|
||||||
if snapshots {
|
if snapshots && scheme == rawdb.HashScheme {
|
||||||
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1850,7 +1850,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err = pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1915,7 +1915,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1952,8 +1952,10 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
|
if _, err := chain.InsertChain(blocks[1:2]); err != nil {
|
||||||
t.Fatalf("Failed to import canonical chain start: %v", err)
|
t.Fatalf("Failed to import canonical chain start: %v", err)
|
||||||
}
|
}
|
||||||
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
if scheme == rawdb.HashScheme {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
|
||||||
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert block B3 and commit the state into disk
|
// Insert block B3 and commit the state into disk
|
||||||
|
|
@ -1973,7 +1975,7 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
chain.stopWithoutSaving()
|
chain.stopWithoutSaving()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err = pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err = pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
t.Fatalf("Failed to reopen persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1997,15 +1999,23 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
expHead := uint64(1)
|
expHead := uint64(1)
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
expHead = uint64(2)
|
// The pathdb database makes sure that snapshot and trie are consistent,
|
||||||
|
// so only the last block is reverted in case of a crash.
|
||||||
|
expHead = uint64(3)
|
||||||
}
|
}
|
||||||
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
|
if head := chain.CurrentBlock(); head.Number.Uint64() != expHead {
|
||||||
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
|
t.Errorf("Head block mismatch: have %d, want %d", head.Number, expHead)
|
||||||
}
|
}
|
||||||
|
if scheme == rawdb.PathScheme {
|
||||||
// Reinsert B2-B4
|
// Reinsert B4
|
||||||
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
if _, err := chain.InsertChain(blocks[3:]); err != nil {
|
||||||
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reinsert B2-B4
|
||||||
|
if _, err := chain.InsertChain(blocks[1:]); err != nil {
|
||||||
|
t.Fatalf("Failed to import canonical chain tail: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
|
||||||
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
|
||||||
|
|
@ -2016,7 +2026,9 @@ func testIssue23496(t *testing.T, scheme string) {
|
||||||
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) {
|
if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) {
|
||||||
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4))
|
t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4))
|
||||||
}
|
}
|
||||||
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
if scheme == rawdb.HashScheme {
|
||||||
t.Error("Failed to regenerate the snapshot of known state")
|
if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
|
||||||
|
t.Error("Failed to regenerate the snapshot of known state")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1969,7 +1969,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -2023,7 +2023,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
}
|
}
|
||||||
if tt.commitBlock > 0 {
|
if tt.commitBlock > 0 {
|
||||||
chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
|
chain.triedb.Commit(canonblocks[tt.commitBlock-1].Root(), false)
|
||||||
if snapshots {
|
if snapshots && scheme == rawdb.HashScheme {
|
||||||
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
if err := chain.snaps.Cap(canonblocks[tt.commitBlock-1].Root(), 0); err != nil {
|
||||||
t.Fatalf("Failed to flatten snapshots: %v", err)
|
t.Fatalf("Failed to flatten snapshots: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := filepath.Join(datadir, "ancient")
|
ancient := filepath.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
||||||
if basic.commitBlock > 0 && basic.commitBlock == point {
|
if basic.commitBlock > 0 && basic.commitBlock == point {
|
||||||
chain.TrieDB().Commit(blocks[point-1].Root(), false)
|
chain.TrieDB().Commit(blocks[point-1].Root(), false)
|
||||||
}
|
}
|
||||||
if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
|
if basic.snapshotBlock > 0 && basic.snapshotBlock == point && basic.scheme == rawdb.HashScheme {
|
||||||
// Flushing the entire snap tree into the disk, the
|
// Flushing the entire snap tree into the disk, the
|
||||||
// relevant (a) snapshot root and (b) snapshot generator
|
// relevant (a) snapshot root and (b) snapshot generator
|
||||||
// will be persisted atomically.
|
// will be persisted atomically.
|
||||||
|
|
@ -149,13 +149,17 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
|
||||||
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
|
block := chain.GetBlockByNumber(basic.expSnapshotBottom)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
|
t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom)
|
||||||
} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
|
} else if basic.scheme == rawdb.HashScheme {
|
||||||
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
|
if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
|
||||||
|
t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the snapshot, ensure it's integrated
|
// Check the snapshot, ensure it's integrated
|
||||||
if err := chain.snaps.Verify(block.Root()); err != nil {
|
if basic.scheme == rawdb.HashScheme {
|
||||||
t.Errorf("The disk layer is not integrated %v", err)
|
if err := chain.snaps.Verify(block.Root()); err != nil {
|
||||||
|
t.Errorf("The disk layer is not integrated %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,7 +261,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||||
chain.triedb.Close()
|
chain.triedb.Close()
|
||||||
|
|
||||||
// Start a new blockchain back up and see where the repair leads us
|
// Start a new blockchain back up and see where the repair leads us
|
||||||
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false, true)
|
pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -565,12 +569,14 @@ func TestHighCommitCrashWithNewSnapshot(t *testing.T) {
|
||||||
//
|
//
|
||||||
// Expected head header : C8
|
// Expected head header : C8
|
||||||
// Expected head fast block: C8
|
// Expected head fast block: C8
|
||||||
// Expected head block : G
|
// Expected head block : G (Hash mode), C6 (Hash mode)
|
||||||
// Expected snapshot disk : C4
|
// Expected snapshot disk : C4 (Hash mode)
|
||||||
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
|
for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} {
|
||||||
expHead := uint64(0)
|
expHead := uint64(0)
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
expHead = uint64(4)
|
// The pathdb database makes sure that snapshot and trie are consistent,
|
||||||
|
// so only the last two blocks are reverted in case of a crash.
|
||||||
|
expHead = uint64(6)
|
||||||
}
|
}
|
||||||
test := &crashSnapshotTest{
|
test := &crashSnapshotTest{
|
||||||
snapshotTestBasic{
|
snapshotTestBasic{
|
||||||
|
|
|
||||||
|
|
@ -734,7 +734,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
// Freezer style fast import the chain.
|
// Freezer style fast import the chain.
|
||||||
|
|
@ -747,7 +747,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(len(blocks)/2)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -871,7 +871,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer fast.Stop()
|
defer fast.Stop()
|
||||||
|
|
||||||
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
if n, err := fast.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
assert(t, "fast", fast, height, height, 0)
|
assert(t, "fast", fast, height, height, 0)
|
||||||
|
|
@ -884,7 +884,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
defer ancient.Stop()
|
defer ancient.Stop()
|
||||||
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
assert(t, "ancient", ancient, height, height, 0)
|
assert(t, "ancient", ancient, height, height, 0)
|
||||||
|
|
@ -1696,7 +1696,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||||
defer ancientDb.Close()
|
defer ancientDb.Close()
|
||||||
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
|
||||||
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
if n, err := ancient.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), uint64(3*len(blocks)/4)); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
|
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
|
||||||
|
|
@ -1991,7 +1991,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||||
}
|
}
|
||||||
} else if typ == "receipts" {
|
} else if typ == "receipts" {
|
||||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
_, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
asserter = func(t *testing.T, block *types.Block) {
|
asserter = func(t *testing.T, block *types.Block) {
|
||||||
|
|
@ -2157,7 +2157,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||||
}
|
}
|
||||||
} else if typ == "receipts" {
|
} else if typ == "receipts" {
|
||||||
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
||||||
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
_, err = chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
asserter = func(t *testing.T, block *types.Block) {
|
asserter = func(t *testing.T, block *types.Block) {
|
||||||
|
|
@ -2492,7 +2492,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
ancient := path.Join(datadir, "ancient")
|
ancient := path.Join(datadir, "ancient")
|
||||||
|
|
||||||
pdb, err := pebble.New(datadir, 0, 0, "", false, true)
|
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -4099,7 +4099,7 @@ func TestEIP7702(t *testing.T) {
|
||||||
// The way the auths are combined, it becomes
|
// The way the auths are combined, it becomes
|
||||||
// 1. tx -> addr1 which is delegated to 0xaaaa
|
// 1. tx -> addr1 which is delegated to 0xaaaa
|
||||||
// 2. addr1:0xaaaa calls into addr2:0xbbbb
|
// 2. addr1:0xaaaa calls into addr2:0xbbbb
|
||||||
// 3. addr2:0xbbbb writes to storage
|
// 3. addr2:0xbbbb writes to storage
|
||||||
auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
|
auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
|
||||||
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
|
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
|
||||||
Address: aa,
|
Address: aa,
|
||||||
|
|
@ -4205,10 +4205,10 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
||||||
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
chain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(rawdb.PathScheme), gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
if n, err := chain.InsertReceiptChain(blocks, receipts, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(blocks, types.EncodeBlockReceiptLists(receipts), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
if n, err := chain.InsertReceiptChain(chainA, receiptsA, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(chainA, types.EncodeBlockReceiptLists(receiptsA), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
// If the common ancestor is below the ancient limit, rewind the chain head.
|
// If the common ancestor is below the ancient limit, rewind the chain head.
|
||||||
|
|
@ -4218,7 +4218,7 @@ func testChainReorgSnapSync(t *testing.T, ancientLimit uint64) {
|
||||||
rawdb.WriteLastPivotNumber(db, ancestor)
|
rawdb.WriteLastPivotNumber(db, ancestor)
|
||||||
chain.SetHead(ancestor)
|
chain.SetHead(ancestor)
|
||||||
}
|
}
|
||||||
if n, err := chain.InsertReceiptChain(chainB, receiptsB, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(chainB, types.EncodeBlockReceiptLists(receiptsB), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
head := chain.CurrentSnapBlock()
|
head := chain.CurrentSnapBlock()
|
||||||
|
|
@ -4336,7 +4336,7 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64,
|
||||||
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
|
if n, err := chain.InsertHeadersBeforeCutoff(headersBefore); err != nil {
|
||||||
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
|
t.Fatalf("failed to insert headers before cutoff %d: %v", n, err)
|
||||||
}
|
}
|
||||||
if n, err := chain.InsertReceiptChain(blocksAfter, receiptsAfter, ancientLimit); err != nil {
|
if n, err := chain.InsertReceiptChain(blocksAfter, types.EncodeBlockReceiptLists(receiptsAfter), ancientLimit); err != nil {
|
||||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||||
}
|
}
|
||||||
headSnap := chain.CurrentSnapBlock()
|
headSnap := chain.CurrentSnapBlock()
|
||||||
|
|
|
||||||
|
|
@ -328,9 +328,13 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
|
||||||
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||||
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
ProcessWithdrawalQueue(&requests, evm)
|
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
|
panic(fmt.Sprintf("could not process withdrawal requests: %v", err))
|
||||||
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
ProcessConsolidationQueue(&requests, evm)
|
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
|
panic(fmt.Sprintf("could not process consolidation requests: %v", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ type blockchain interface {
|
||||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||||
GetCanonicalHash(number uint64) common.Hash
|
GetCanonicalHash(number uint64) common.Hash
|
||||||
GetReceiptsByHash(hash common.Hash) types.Receipts
|
GetReceiptsByHash(hash common.Hash) types.Receipts
|
||||||
|
GetRawReceipts(hash common.Hash, number uint64) types.Receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainView represents an immutable view of a chain with a block id and a set
|
// ChainView represents an immutable view of a chain with a block id and a set
|
||||||
|
|
@ -58,47 +59,80 @@ func NewChainView(chain blockchain, number uint64, hash common.Hash) *ChainView
|
||||||
return cv
|
return cv
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBlockHash returns the block hash belonging to the given block number.
|
// HeadNumber returns the head block number of the chain view.
|
||||||
|
func (cv *ChainView) HeadNumber() uint64 {
|
||||||
|
return cv.headNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockHash returns the block hash belonging to the given block number.
|
||||||
// Note that the hash of the head block is not returned because ChainView might
|
// Note that the hash of the head block is not returned because ChainView might
|
||||||
// represent a view where the head block is currently being created.
|
// represent a view where the head block is currently being created.
|
||||||
func (cv *ChainView) getBlockHash(number uint64) common.Hash {
|
func (cv *ChainView) BlockHash(number uint64) common.Hash {
|
||||||
if number >= cv.headNumber {
|
cv.lock.Lock()
|
||||||
|
defer cv.lock.Unlock()
|
||||||
|
|
||||||
|
if number > cv.headNumber {
|
||||||
panic("invalid block number")
|
panic("invalid block number")
|
||||||
}
|
}
|
||||||
return cv.blockHash(number)
|
return cv.blockHash(number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBlockId returns the unique block id belonging to the given block number.
|
// BlockId returns the unique block id belonging to the given block number.
|
||||||
// Note that it is currently equal to the block hash. In the future it might
|
// Note that it is currently equal to the block hash. In the future it might
|
||||||
// be a different id for future blocks if the log index root becomes part of
|
// be a different id for future blocks if the log index root becomes part of
|
||||||
// consensus and therefore rendering the index with the new head will happen
|
// consensus and therefore rendering the index with the new head will happen
|
||||||
// before the hash of that new head is available.
|
// before the hash of that new head is available.
|
||||||
func (cv *ChainView) getBlockId(number uint64) common.Hash {
|
func (cv *ChainView) BlockId(number uint64) common.Hash {
|
||||||
|
cv.lock.Lock()
|
||||||
|
defer cv.lock.Unlock()
|
||||||
|
|
||||||
if number > cv.headNumber {
|
if number > cv.headNumber {
|
||||||
panic("invalid block number")
|
panic("invalid block number")
|
||||||
}
|
}
|
||||||
return cv.blockHash(number)
|
return cv.blockHash(number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getReceipts returns the set of receipts belonging to the block at the given
|
// Header returns the block header at the given block number.
|
||||||
|
func (cv *ChainView) Header(number uint64) *types.Header {
|
||||||
|
return cv.chain.GetHeader(cv.BlockHash(number), number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receipts returns the set of receipts belonging to the block at the given
|
||||||
// block number.
|
// block number.
|
||||||
func (cv *ChainView) getReceipts(number uint64) types.Receipts {
|
func (cv *ChainView) Receipts(number uint64) types.Receipts {
|
||||||
if number > cv.headNumber {
|
blockHash := cv.BlockHash(number)
|
||||||
panic("invalid block number")
|
|
||||||
}
|
|
||||||
blockHash := cv.blockHash(number)
|
|
||||||
if blockHash == (common.Hash{}) {
|
if blockHash == (common.Hash{}) {
|
||||||
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return cv.chain.GetReceiptsByHash(blockHash)
|
return cv.chain.GetReceiptsByHash(blockHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// limitedView returns a new chain view that is a truncated version of the parent view.
|
// RawReceipts returns the set of receipts belonging to the block at the given
|
||||||
func (cv *ChainView) limitedView(newHead uint64) *ChainView {
|
// block number. Does not derive the fields of the receipts, should only be
|
||||||
if newHead >= cv.headNumber {
|
// used during creation of the filter maps, please use cv.Receipts during querying.
|
||||||
return cv
|
func (cv *ChainView) RawReceipts(number uint64) types.Receipts {
|
||||||
|
blockHash := cv.BlockHash(number)
|
||||||
|
if blockHash == (common.Hash{}) {
|
||||||
|
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return NewChainView(cv.chain, newHead, cv.blockHash(newHead))
|
return cv.chain.GetRawReceipts(blockHash, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SharedRange returns the block range shared by two chain views.
|
||||||
|
func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
||||||
|
cv.lock.Lock()
|
||||||
|
defer cv.lock.Unlock()
|
||||||
|
|
||||||
|
if cv == nil || cv2 == nil || !cv.extendNonCanonical() || !cv2.extendNonCanonical() {
|
||||||
|
return common.Range[uint64]{}
|
||||||
|
}
|
||||||
|
var sharedLen uint64
|
||||||
|
for n := min(cv.headNumber+1-uint64(len(cv.hashes)), cv2.headNumber+1-uint64(len(cv2.hashes))); n <= cv.headNumber && n <= cv2.headNumber && cv.blockHash(n) == cv2.blockHash(n); n++ {
|
||||||
|
sharedLen = n + 1
|
||||||
|
}
|
||||||
|
return common.NewRange(0, sharedLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// equalViews returns true if the two chain views are equivalent.
|
// equalViews returns true if the two chain views are equivalent.
|
||||||
|
|
@ -106,7 +140,7 @@ func equalViews(cv1, cv2 *ChainView) bool {
|
||||||
if cv1 == nil || cv2 == nil {
|
if cv1 == nil || cv2 == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return cv1.headNumber == cv2.headNumber && cv1.getBlockId(cv1.headNumber) == cv2.getBlockId(cv2.headNumber)
|
return cv1.headNumber == cv2.headNumber && cv1.BlockId(cv1.headNumber) == cv2.BlockId(cv2.headNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchViews returns true if the two chain views are equivalent up until the
|
// matchViews returns true if the two chain views are equivalent up until the
|
||||||
|
|
@ -120,9 +154,9 @@ func matchViews(cv1, cv2 *ChainView, number uint64) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if number == cv1.headNumber || number == cv2.headNumber {
|
if number == cv1.headNumber || number == cv2.headNumber {
|
||||||
return cv1.getBlockId(number) == cv2.getBlockId(number)
|
return cv1.BlockId(number) == cv2.BlockId(number)
|
||||||
}
|
}
|
||||||
return cv1.getBlockHash(number) == cv2.getBlockHash(number)
|
return cv1.BlockHash(number) == cv2.BlockHash(number)
|
||||||
}
|
}
|
||||||
|
|
||||||
// extendNonCanonical checks whether the previously known reverse list of head
|
// extendNonCanonical checks whether the previously known reverse list of head
|
||||||
|
|
@ -150,9 +184,6 @@ func (cv *ChainView) extendNonCanonical() bool {
|
||||||
|
|
||||||
// blockHash returns the given block hash without doing the head number check.
|
// blockHash returns the given block hash without doing the head number check.
|
||||||
func (cv *ChainView) blockHash(number uint64) common.Hash {
|
func (cv *ChainView) blockHash(number uint64) common.Hash {
|
||||||
cv.lock.Lock()
|
|
||||||
defer cv.lock.Unlock()
|
|
||||||
|
|
||||||
if number+uint64(len(cv.hashes)) <= cv.headNumber {
|
if number+uint64(len(cv.hashes)) <= cv.headNumber {
|
||||||
hash := cv.chain.GetCanonicalHash(number)
|
hash := cv.chain.GetCanonicalHash(number)
|
||||||
if !cv.extendNonCanonical() {
|
if !cv.extendNonCanonical() {
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,6 @@
|
||||||
{"blockNumber": 3101669, "blockId": "0xa6e66a18fed64d33178c7088f273c404be597662c34f6e6884cf2e24f3ea4ace", "firstIndex": 1073741353},
|
{"blockNumber": 3101669, "blockId": "0xa6e66a18fed64d33178c7088f273c404be597662c34f6e6884cf2e24f3ea4ace", "firstIndex": 1073741353},
|
||||||
{"blockNumber": 3221725, "blockId": "0xe771f897dece48b1583cc1d1d10de8015da57407eb1fdf239fdbe46eaab85143", "firstIndex": 1140850137},
|
{"blockNumber": 3221725, "blockId": "0xe771f897dece48b1583cc1d1d10de8015da57407eb1fdf239fdbe46eaab85143", "firstIndex": 1140850137},
|
||||||
{"blockNumber": 3357164, "blockId": "0x6252d0aa54c79623b0680069c88d7b5c47983f0d5c4845b6c811b8d9b5e8ff3c", "firstIndex": 1207959453},
|
{"blockNumber": 3357164, "blockId": "0x6252d0aa54c79623b0680069c88d7b5c47983f0d5c4845b6c811b8d9b5e8ff3c", "firstIndex": 1207959453},
|
||||||
{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542}
|
{"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542},
|
||||||
|
{"blockNumber": 3546397, "blockId": "0xdabdef7defa4281180a57c5af121877b82274f15ccf074ea0096146f4c246df2", "firstIndex": 1342176778}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -260,5 +260,12 @@
|
||||||
{"blockNumber": 21959188, "blockId": "0x6b9c482cc4822af2ad62a359b923062556ab6a046d1a39a8243b764848690601", "firstIndex": 17381193886},
|
{"blockNumber": 21959188, "blockId": "0x6b9c482cc4822af2ad62a359b923062556ab6a046d1a39a8243b764848690601", "firstIndex": 17381193886},
|
||||||
{"blockNumber": 21994911, "blockId": "0x0d99ef9dd42dd1c62fc5249eb4f618e7672ab93fa0ba7545c77360371cf972e5", "firstIndex": 17448302795},
|
{"blockNumber": 21994911, "blockId": "0x0d99ef9dd42dd1c62fc5249eb4f618e7672ab93fa0ba7545c77360371cf972e5", "firstIndex": 17448302795},
|
||||||
{"blockNumber": 22026007, "blockId": "0xf7cdc7f6694f2c2ed31fa8a607f65cfa59d0dd7d7424ab5c017f959ae2982c71", "firstIndex": 17515413036},
|
{"blockNumber": 22026007, "blockId": "0xf7cdc7f6694f2c2ed31fa8a607f65cfa59d0dd7d7424ab5c017f959ae2982c71", "firstIndex": 17515413036},
|
||||||
{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768}
|
{"blockNumber": 22059890, "blockId": "0x82b768a0dddefda2eefd3a33596ea2be075312f1dd4b01f6b0d377faca2af98b", "firstIndex": 17582521768},
|
||||||
|
{"blockNumber": 22090784, "blockId": "0xf97c2eaf9a550360ac24000c0ff17ffa388a2bdd6f73f2f36718e332edfa107a", "firstIndex": 17649630983},
|
||||||
|
{"blockNumber": 22121157, "blockId": "0xa790025235db782e899f23d8b09663ec2d74ec149e4125d62989f98829b08e2d", "firstIndex": 17716724973},
|
||||||
|
{"blockNumber": 22148056, "blockId": "0xbe25ac4f1bdd89a7db5782bf1157e6c4378d80220d67a029397853ef16cd1a4c", "firstIndex": 17783838366},
|
||||||
|
{"blockNumber": 22168652, "blockId": "0x6ae43618c915e636794e2cc2d75dde9992766881c7405fe6479c045ed4bee57e", "firstIndex": 17850956277},
|
||||||
|
{"blockNumber": 22190975, "blockId": "0x9437121647899a4b7b84d67fbea7cc6ff967481c2eab4328ccd86e2cefe19420", "firstIndex": 17918066140},
|
||||||
|
{"blockNumber": 22234357, "blockId": "0x036030830134f9224160d5a0b62da35ec7813dc8855d554bd22e9d38545243ed", "firstIndex": 17985175075},
|
||||||
|
{"blockNumber": 22276736, "blockId": "0x5ceb96d98aa1b4c1c2f2fa253ae9cdb1b04e0420c11bf795400e8762c0a1635c", "firstIndex": 18052284344}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -64,5 +64,9 @@
|
||||||
{"blockNumber": 7730079, "blockId": "0x08e0d511a93e2a9c004b3456d77d687ae86247417ada1cdd1f85332a62dfed1d", "firstIndex": 4227846795},
|
{"blockNumber": 7730079, "blockId": "0x08e0d511a93e2a9c004b3456d77d687ae86247417ada1cdd1f85332a62dfed1d", "firstIndex": 4227846795},
|
||||||
{"blockNumber": 7777515, "blockId": "0xeba8faed2e12bb9ed5e7fad19dd82662f15f6f4f512a0d232eedf1e676712428", "firstIndex": 4294966082},
|
{"blockNumber": 7777515, "blockId": "0xeba8faed2e12bb9ed5e7fad19dd82662f15f6f4f512a0d232eedf1e676712428", "firstIndex": 4294966082},
|
||||||
{"blockNumber": 7828860, "blockId": "0x62c054bd24be351dc4777460ee922a75fdcea5c9830455cbac6fa29552719c85", "firstIndex": 4362076087},
|
{"blockNumber": 7828860, "blockId": "0x62c054bd24be351dc4777460ee922a75fdcea5c9830455cbac6fa29552719c85", "firstIndex": 4362076087},
|
||||||
{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267}
|
{"blockNumber": 7869890, "blockId": "0x5664bcfa6151f798781e22bea4f9a2c796d097402350cb131e4e3c78e13e461a", "firstIndex": 4429183267},
|
||||||
|
{"blockNumber": 7911722, "blockId": "0x9a85e48e3135c97c51fc1786f2af0596c802e021b6c53cfca65a129cafcd23ed", "firstIndex": 4496287265},
|
||||||
|
{"blockNumber": 7960147, "blockId": "0xc9359cc76d7090e1c8a031108f0ab7a8935d971efd4325fe53612a1d99562f6f", "firstIndex": 4563402388},
|
||||||
|
{"blockNumber": 8030418, "blockId": "0x21867e68cd8327aed2da2601399d60f7f9e41dca4a4f2f9be982e5a2b9304a88", "firstIndex": 4630511616},
|
||||||
|
{"blockNumber": 8087701, "blockId": "0x0fa8c8d7549cc9a8d308262706fe248efe759f8b63511efb1e7f3926e9af2dcb", "firstIndex": 4697614758}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
databaseVersion = 1 // reindexed if database version does not match
|
databaseVersion = 2 // reindexed if database version does not match
|
||||||
cachedLastBlocks = 1000 // last block of map pointers
|
cachedLastBlocks = 1000 // last block of map pointers
|
||||||
cachedLvPointers = 1000 // first log value pointer of block pointers
|
cachedLvPointers = 1000 // first log value pointer of block pointers
|
||||||
cachedBaseRows = 100 // groups of base layer filter row data
|
cachedBaseRows = 100 // groups of base layer filter row data
|
||||||
|
|
@ -85,11 +85,17 @@ type FilterMaps struct {
|
||||||
// fields written by the indexer and read by matcher backend. Indexer can
|
// fields written by the indexer and read by matcher backend. Indexer can
|
||||||
// read them without a lock and write them under indexLock write lock.
|
// read them without a lock and write them under indexLock write lock.
|
||||||
// Matcher backend can read them under indexLock read lock.
|
// Matcher backend can read them under indexLock read lock.
|
||||||
indexLock sync.RWMutex
|
indexLock sync.RWMutex
|
||||||
indexedRange filterMapsRange
|
indexedRange filterMapsRange
|
||||||
cleanedEpochsBefore uint32 // all unindexed data cleaned before this point
|
indexedView *ChainView // always consistent with the log index
|
||||||
indexedView *ChainView // always consistent with the log index
|
hasTempRange bool
|
||||||
hasTempRange bool
|
|
||||||
|
// cleanedEpochsBefore indicates that all unindexed data before this point
|
||||||
|
// has been cleaned.
|
||||||
|
//
|
||||||
|
// This field is only accessed and modified within tryUnindexTail, so no
|
||||||
|
// explicit locking is required.
|
||||||
|
cleanedEpochsBefore uint32
|
||||||
|
|
||||||
// also accessed by indexer and matcher backend but no locking needed.
|
// also accessed by indexer and matcher backend but no locking needed.
|
||||||
filterMapCache *lru.Cache[uint32, filterMap]
|
filterMapCache *lru.Cache[uint32, filterMap]
|
||||||
|
|
@ -128,6 +134,7 @@ type FilterMaps struct {
|
||||||
|
|
||||||
// test hooks
|
// test hooks
|
||||||
testDisableSnapshots, testSnapshotUsed bool
|
testDisableSnapshots, testSnapshotUsed bool
|
||||||
|
testProcessEventsHook func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterMap is a full or partial in-memory representation of a filter map where
|
// filterMap is a full or partial in-memory representation of a filter map where
|
||||||
|
|
@ -237,6 +244,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
||||||
disabledCh: make(chan struct{}),
|
disabledCh: make(chan struct{}),
|
||||||
exportFileName: config.ExportFileName,
|
exportFileName: config.ExportFileName,
|
||||||
Params: params,
|
Params: params,
|
||||||
|
targetView: initView,
|
||||||
|
indexedView: initView,
|
||||||
indexedRange: filterMapsRange{
|
indexedRange: filterMapsRange{
|
||||||
initialized: initialized,
|
initialized: initialized,
|
||||||
headIndexed: rs.HeadIndexed,
|
headIndexed: rs.HeadIndexed,
|
||||||
|
|
@ -247,26 +256,19 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
||||||
},
|
},
|
||||||
// deleting last unindexed epoch might have been interrupted by shutdown
|
// deleting last unindexed epoch might have been interrupted by shutdown
|
||||||
cleanedEpochsBefore: max(rs.MapsFirst>>params.logMapsPerEpoch, 1) - 1,
|
cleanedEpochsBefore: max(rs.MapsFirst>>params.logMapsPerEpoch, 1) - 1,
|
||||||
historyCutoff: historyCutoff,
|
|
||||||
finalBlock: finalBlock,
|
|
||||||
matcherSyncCh: make(chan *FilterMapsMatcherBackend),
|
|
||||||
matchers: make(map[*FilterMapsMatcherBackend]struct{}),
|
|
||||||
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
|
||||||
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
|
||||||
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
|
||||||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
|
||||||
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial indexer target.
|
historyCutoff: historyCutoff,
|
||||||
f.targetView = initView
|
finalBlock: finalBlock,
|
||||||
if f.indexedRange.initialized {
|
matcherSyncCh: make(chan *FilterMapsMatcherBackend),
|
||||||
f.indexedView = f.initChainView(f.targetView)
|
matchers: make(map[*FilterMapsMatcherBackend]struct{}),
|
||||||
f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.headNumber+1
|
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
||||||
if !f.indexedRange.headIndexed {
|
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
||||||
f.indexedRange.headDelimiter = 0
|
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
||||||
}
|
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
||||||
|
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
||||||
}
|
}
|
||||||
|
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
|
||||||
|
|
||||||
if f.indexedRange.hasIndexedBlocks() {
|
if f.indexedRange.hasIndexedBlocks() {
|
||||||
log.Info("Initialized log indexer",
|
log.Info("Initialized log indexer",
|
||||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||||
|
|
@ -295,29 +297,40 @@ func (f *FilterMaps) Stop() {
|
||||||
f.closeWg.Wait()
|
f.closeWg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// initChainView returns a chain view consistent with both the current target
|
// checkRevertRange checks whether the existing index is consistent with the
|
||||||
// view and the current state of the log index as found in the database, based
|
// current indexed view and reverts inconsistent maps if necessary.
|
||||||
// on the last block of stored maps.
|
func (f *FilterMaps) checkRevertRange() {
|
||||||
// Note that the returned view might be shorter than the existing index if
|
if f.indexedRange.maps.Count() == 0 {
|
||||||
// the latest maps are not consistent with targetView.
|
return
|
||||||
func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
|
}
|
||||||
mapIndex := f.indexedRange.maps.AfterLast()
|
lastMap := f.indexedRange.maps.Last()
|
||||||
for {
|
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap)
|
||||||
var ok bool
|
if err != nil {
|
||||||
mapIndex, ok = f.lastMapBoundaryBefore(mapIndex)
|
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||||
if !ok {
|
f.reset()
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex)
|
for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId {
|
||||||
if err != nil {
|
// revert last map
|
||||||
log.Error("Could not initialize indexed chain view", "error", err)
|
if f.indexedRange.maps.Count() == 1 {
|
||||||
break
|
f.reset() // reset database if no rendered maps remained
|
||||||
}
|
return
|
||||||
if lastBlockNumber <= chainView.headNumber && chainView.getBlockId(lastBlockNumber) == lastBlockId {
|
}
|
||||||
return chainView.limitedView(lastBlockNumber)
|
lastMap--
|
||||||
}
|
newRange := f.indexedRange
|
||||||
|
newRange.maps.SetLast(lastMap)
|
||||||
|
lastBlockNumber, lastBlockId, err = f.getLastBlockOfMap(lastMap)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||||
|
f.reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newRange.blocks.SetAfterLast(lastBlockNumber) // lastBlockNumber is probably partially indexed
|
||||||
|
newRange.headIndexed = false
|
||||||
|
newRange.headDelimiter = 0
|
||||||
|
// only shorten range and leave map data; next head render will overwrite it
|
||||||
|
f.setRange(f.db, f.indexedView, newRange, false)
|
||||||
}
|
}
|
||||||
return chainView.limitedView(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset un-initializes the FilterMaps structure and removes all related data from
|
// reset un-initializes the FilterMaps structure and removes all related data from
|
||||||
|
|
@ -370,7 +383,7 @@ func (f *FilterMaps) init() error {
|
||||||
for min < max {
|
for min < max {
|
||||||
mid := (min + max + 1) / 2
|
mid := (min + max + 1) / 2
|
||||||
cp := checkpointList[mid-1]
|
cp := checkpointList[mid-1]
|
||||||
if cp.BlockNumber <= f.targetView.headNumber && f.targetView.getBlockId(cp.BlockNumber) == cp.BlockId {
|
if cp.BlockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(cp.BlockNumber) == cp.BlockId {
|
||||||
min = mid
|
min = mid
|
||||||
} else {
|
} else {
|
||||||
max = mid - 1
|
max = mid - 1
|
||||||
|
|
@ -443,6 +456,7 @@ func (f *FilterMaps) safeDeleteWithLogs(deleteFn func(db ethdb.KeyValueStore, ha
|
||||||
|
|
||||||
// setRange updates the indexed chain view and covered range and also adds the
|
// setRange updates the indexed chain view and covered range and also adds the
|
||||||
// changes to the given batch.
|
// changes to the given batch.
|
||||||
|
//
|
||||||
// Note that this function assumes that the index write lock is being held.
|
// Note that this function assumes that the index write lock is being held.
|
||||||
func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, newRange filterMapsRange, isTempRange bool) {
|
func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, newRange filterMapsRange, isTempRange bool) {
|
||||||
f.indexedView = newView
|
f.indexedView = newView
|
||||||
|
|
@ -476,6 +490,7 @@ func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, ne
|
||||||
// Note that this function assumes that the log index structure is consistent
|
// Note that this function assumes that the log index structure is consistent
|
||||||
// with the canonical chain at the point where the given log value index points.
|
// with the canonical chain at the point where the given log value index points.
|
||||||
// If this is not the case then an invalid result or an error may be returned.
|
// If this is not the case then an invalid result or an error may be returned.
|
||||||
|
//
|
||||||
// Note that this function assumes that the indexer read lock is being held when
|
// Note that this function assumes that the indexer read lock is being held when
|
||||||
// called from outside the indexerLoop goroutine.
|
// called from outside the indexerLoop goroutine.
|
||||||
func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
|
func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
|
||||||
|
|
@ -512,7 +527,7 @@ func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// get block receipts
|
// get block receipts
|
||||||
receipts := f.indexedView.getReceipts(firstBlockNumber)
|
receipts := f.indexedView.Receipts(firstBlockNumber)
|
||||||
if receipts == nil {
|
if receipts == nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err)
|
return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err)
|
||||||
}
|
}
|
||||||
|
|
@ -573,7 +588,7 @@ func (f *FilterMaps) getFilterMapRow(mapIndex, rowIndex uint32, baseLayerOnly bo
|
||||||
}
|
}
|
||||||
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
||||||
}
|
}
|
||||||
baseRow := baseRows[mapIndex&(f.baseRowGroupLength-1)]
|
baseRow := slices.Clone(baseRows[mapIndex&(f.baseRowGroupLength-1)])
|
||||||
if baseLayerOnly {
|
if baseLayerOnly {
|
||||||
return baseRow, nil
|
return baseRow, nil
|
||||||
}
|
}
|
||||||
|
|
@ -610,7 +625,9 @@ func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []u
|
||||||
if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced
|
if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced
|
||||||
var ok bool
|
var ok bool
|
||||||
baseRows, ok = f.baseRowsCache.Get(baseMapRowIndex)
|
baseRows, ok = f.baseRowsCache.Get(baseMapRowIndex)
|
||||||
if !ok {
|
if ok {
|
||||||
|
baseRows = slices.Clone(baseRows)
|
||||||
|
} else {
|
||||||
var err error
|
var err error
|
||||||
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -650,14 +667,11 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBlockLvPointer returns the starting log value index where the log values
|
// getBlockLvPointer returns the starting log value index where the log values
|
||||||
// generated by the given block are located. If blockNumber is beyond the current
|
// generated by the given block are located.
|
||||||
// head then the first unoccupied log value index is returned.
|
//
|
||||||
// Note that this function assumes that the indexer read lock is being held when
|
// Note that this function assumes that the indexer read lock is being held when
|
||||||
// called from outside the indexerLoop goroutine.
|
// called from outside the indexerLoop goroutine.
|
||||||
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
|
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
|
||||||
if blockNumber >= f.indexedRange.blocks.AfterLast() && f.indexedRange.headIndexed {
|
|
||||||
return f.indexedRange.headDelimiter, nil
|
|
||||||
}
|
|
||||||
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
|
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
|
||||||
return lvPointer, nil
|
return lvPointer, nil
|
||||||
}
|
}
|
||||||
|
|
@ -759,7 +773,7 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) {
|
||||||
return false, errors.New("invalid tail epoch number")
|
return false, errors.New("invalid tail epoch number")
|
||||||
}
|
}
|
||||||
// remove index data
|
// remove index data
|
||||||
if err := f.safeDeleteWithLogs(func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error {
|
deleteFn := func(db ethdb.KeyValueStore, hashScheme bool, stopCb func(bool) bool) error {
|
||||||
first := f.mapRowIndex(firstMap, 0)
|
first := f.mapRowIndex(firstMap, 0)
|
||||||
count := f.mapRowIndex(firstMap+f.mapsPerEpoch, 0) - first
|
count := f.mapRowIndex(firstMap+f.mapsPerEpoch, 0) - first
|
||||||
if err := rawdb.DeleteFilterMapRows(f.db, common.NewRange(first, count), hashScheme, stopCb); err != nil {
|
if err := rawdb.DeleteFilterMapRows(f.db, common.NewRange(first, count), hashScheme, stopCb); err != nil {
|
||||||
|
|
@ -783,10 +797,13 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) {
|
||||||
f.lvPointerCache.Remove(blockNumber)
|
f.lvPointerCache.Remove(blockNumber)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, fmt.Sprintf("Deleting tail epoch #%d", epoch), func() bool {
|
}
|
||||||
|
action := fmt.Sprintf("Deleting tail epoch #%d", epoch)
|
||||||
|
stopFn := func() bool {
|
||||||
f.processEvents()
|
f.processEvents()
|
||||||
return f.stop || !f.targetHeadIndexed()
|
return f.stop || !f.targetHeadIndexed()
|
||||||
}); err == nil {
|
}
|
||||||
|
if err := f.safeDeleteWithLogs(deleteFn, action, stopFn); err == nil {
|
||||||
// everything removed; mark as cleaned and report success
|
// everything removed; mark as cleaned and report success
|
||||||
if f.cleanedEpochsBefore == epoch {
|
if f.cleanedEpochsBefore == epoch {
|
||||||
f.cleanedEpochsBefore = epoch + 1
|
f.cleanedEpochsBefore = epoch + 1
|
||||||
|
|
@ -805,6 +822,9 @@ func (f *FilterMaps) deleteTailEpoch(epoch uint32) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// exportCheckpoints exports epoch checkpoints in the format used by checkpoints.go.
|
// exportCheckpoints exports epoch checkpoints in the format used by checkpoints.go.
|
||||||
|
//
|
||||||
|
// Note: acquiring the indexLock read lock is unnecessary here, as this function
|
||||||
|
// is always called within the indexLoop.
|
||||||
func (f *FilterMaps) exportCheckpoints() {
|
func (f *FilterMaps) exportCheckpoints() {
|
||||||
finalLvPtr, err := f.getBlockLvPointer(f.finalBlock + 1)
|
finalLvPtr, err := f.getBlockLvPointer(f.finalBlock + 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,10 @@ func (f *FilterMaps) indexerLoop() {
|
||||||
log.Info("Started log indexer")
|
log.Info("Started log indexer")
|
||||||
|
|
||||||
for !f.stop {
|
for !f.stop {
|
||||||
|
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||||
|
// as the `indexedRange` is accessed within the indexerLoop.
|
||||||
if !f.indexedRange.initialized {
|
if !f.indexedRange.initialized {
|
||||||
if f.targetView.headNumber == 0 {
|
if f.targetView.HeadNumber() == 0 {
|
||||||
// initialize when chain head is available
|
// initialize when chain head is available
|
||||||
f.processSingleEvent(true)
|
f.processSingleEvent(true)
|
||||||
continue
|
continue
|
||||||
|
|
@ -105,7 +107,7 @@ type targetUpdate struct {
|
||||||
historyCutoff, finalBlock uint64
|
historyCutoff, finalBlock uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTargetView sets a new target chain view for the indexer to render.
|
// SetTarget sets a new target chain view for the indexer to render.
|
||||||
// Note that SetTargetView never blocks.
|
// Note that SetTargetView never blocks.
|
||||||
func (f *FilterMaps) SetTarget(targetView *ChainView, historyCutoff, finalBlock uint64) {
|
func (f *FilterMaps) SetTarget(targetView *ChainView, historyCutoff, finalBlock uint64) {
|
||||||
if targetView == nil {
|
if targetView == nil {
|
||||||
|
|
@ -165,6 +167,9 @@ func (f *FilterMaps) waitForNewHead() {
|
||||||
// processEvents processes all events, blocking only if a block processing is
|
// processEvents processes all events, blocking only if a block processing is
|
||||||
// happening and indexing should be suspended.
|
// happening and indexing should be suspended.
|
||||||
func (f *FilterMaps) processEvents() {
|
func (f *FilterMaps) processEvents() {
|
||||||
|
if f.testProcessEventsHook != nil {
|
||||||
|
f.testProcessEventsHook()
|
||||||
|
}
|
||||||
for f.processSingleEvent(f.blockProcessing) {
|
for f.processSingleEvent(f.blockProcessing) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,6 +180,8 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool {
|
||||||
if f.stop {
|
if f.stop {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||||
|
// as this function is always called within the indexLoop.
|
||||||
if !f.hasTempRange {
|
if !f.hasTempRange {
|
||||||
for _, mb := range f.matcherSyncRequests {
|
for _, mb := range f.matcherSyncRequests {
|
||||||
mb.synced()
|
mb.synced()
|
||||||
|
|
@ -247,9 +254,9 @@ func (f *FilterMaps) tryIndexHead() error {
|
||||||
((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) ||
|
((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) ||
|
||||||
time.Since(f.lastLogHeadIndex) > logFrequency) {
|
time.Since(f.lastLogHeadIndex) > logFrequency) {
|
||||||
log.Info("Log index head rendering in progress",
|
log.Info("Log index head rendering in progress",
|
||||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||||
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
||||||
"remaining", f.indexedView.headNumber-f.indexedRange.blocks.Last(),
|
"remaining", f.indexedView.HeadNumber()-f.indexedRange.blocks.Last(),
|
||||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||||
f.loggedHeadIndex = true
|
f.loggedHeadIndex = true
|
||||||
f.lastLogHeadIndex = time.Now()
|
f.lastLogHeadIndex = time.Now()
|
||||||
|
|
@ -259,7 +266,7 @@ func (f *FilterMaps) tryIndexHead() error {
|
||||||
}
|
}
|
||||||
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
|
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
|
||||||
log.Info("Log index head rendering finished",
|
log.Info("Log index head rendering finished",
|
||||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||||
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
||||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +323,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
|
||||||
if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.blocks.First() &&
|
if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.blocks.First() &&
|
||||||
(!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) {
|
(!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) {
|
||||||
log.Info("Log index tail rendering in progress",
|
log.Info("Log index tail rendering in progress",
|
||||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
"firstblock", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||||
"processed", f.ptrTailIndex-f.indexedRange.blocks.First()+tpb,
|
"processed", f.ptrTailIndex-f.indexedRange.blocks.First()+tpb,
|
||||||
"remaining", remaining,
|
"remaining", remaining,
|
||||||
"next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch,
|
"next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch,
|
||||||
|
|
@ -339,7 +346,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
|
||||||
}
|
}
|
||||||
if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() {
|
if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() {
|
||||||
log.Info("Log index tail rendering finished",
|
log.Info("Log index tail rendering finished",
|
||||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||||
"processed", f.ptrTailIndex-f.indexedRange.blocks.First(),
|
"processed", f.ptrTailIndex-f.indexedRange.blocks.First(),
|
||||||
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
|
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
|
||||||
f.loggedTailIndex = false
|
f.loggedTailIndex = false
|
||||||
|
|
@ -418,10 +425,10 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool {
|
||||||
// tailTargetBlock returns the target value for the tail block number according
|
// tailTargetBlock returns the target value for the tail block number according
|
||||||
// to the log history parameter and the current index head.
|
// to the log history parameter and the current index head.
|
||||||
func (f *FilterMaps) tailTargetBlock() uint64 {
|
func (f *FilterMaps) tailTargetBlock() uint64 {
|
||||||
if f.history == 0 || f.indexedView.headNumber < f.history {
|
if f.history == 0 || f.indexedView.HeadNumber() < f.history {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return f.indexedView.headNumber + 1 - f.history
|
return f.indexedView.HeadNumber() + 1 - f.history
|
||||||
}
|
}
|
||||||
|
|
||||||
// tailPartialBlocks returns the number of rendered blocks in the partially
|
// tailPartialBlocks returns the number of rendered blocks in the partially
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,58 @@ func testIndexerMatcherView(t *testing.T, concurrentRead bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLogsByIndex(t *testing.T) {
|
||||||
|
ts := newTestSetup(t)
|
||||||
|
defer func() {
|
||||||
|
ts.fm.testProcessEventsHook = nil
|
||||||
|
ts.close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
ts.chain.addBlocks(1000, 10, 3, 4, true)
|
||||||
|
ts.setHistory(0, false)
|
||||||
|
ts.fm.WaitIdle()
|
||||||
|
firstLog := make([]uint64, 1001) // first valid log position per block
|
||||||
|
lastLog := make([]uint64, 1001) // last valid log position per block
|
||||||
|
for i := uint64(0); i <= ts.fm.indexedRange.headDelimiter; i++ {
|
||||||
|
log, err := ts.fm.getLogByLvIndex(i)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting log by index %d: %v", i, err)
|
||||||
|
}
|
||||||
|
if log != nil {
|
||||||
|
if firstLog[log.BlockNumber] == 0 {
|
||||||
|
firstLog[log.BlockNumber] = i
|
||||||
|
}
|
||||||
|
lastLog[log.BlockNumber] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var failed bool
|
||||||
|
ts.fm.testProcessEventsHook = func() {
|
||||||
|
if ts.fm.indexedRange.blocks.IsEmpty() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lvi := firstLog[ts.fm.indexedRange.blocks.First()]; lvi != 0 {
|
||||||
|
log, err := ts.fm.getLogByLvIndex(lvi)
|
||||||
|
if log == nil || err != nil {
|
||||||
|
t.Errorf("Error getting first log of indexed block range: %v", err)
|
||||||
|
failed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lvi := lastLog[ts.fm.indexedRange.blocks.Last()]; lvi != 0 {
|
||||||
|
log, err := ts.fm.getLogByLvIndex(lvi)
|
||||||
|
if log == nil || err != nil {
|
||||||
|
t.Errorf("Error getting last log of indexed block range: %v", err)
|
||||||
|
failed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chain := ts.chain.getCanonicalChain()
|
||||||
|
for i := 0; i < 1000 && !failed; i++ {
|
||||||
|
head := rand.Intn(len(chain))
|
||||||
|
ts.chain.setCanonicalChain(chain[:head+1])
|
||||||
|
ts.fm.WaitIdle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIndexerCompareDb(t *testing.T) {
|
func TestIndexerCompareDb(t *testing.T) {
|
||||||
ts := newTestSetup(t)
|
ts := newTestSetup(t)
|
||||||
defer ts.close()
|
defer ts.close()
|
||||||
|
|
@ -463,6 +515,13 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return tc.receipts[hash]
|
return tc.receipts[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc *testChain) GetRawReceipts(hash common.Hash, number uint64) types.Receipts {
|
||||||
|
tc.lock.RLock()
|
||||||
|
defer tc.lock.RUnlock()
|
||||||
|
|
||||||
|
return tc.receipts[hash]
|
||||||
|
}
|
||||||
|
|
||||||
func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) {
|
func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) {
|
||||||
tc.lock.Lock()
|
tc.lock.Lock()
|
||||||
blockGen := func(i int, gen *core.BlockGen) {
|
blockGen := func(i int, gen *core.BlockGen) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -107,7 +108,7 @@ func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, erro
|
||||||
filterMap: cp.filterMap.fullCopy(),
|
filterMap: cp.filterMap.fullCopy(),
|
||||||
mapIndex: cp.mapIndex,
|
mapIndex: cp.mapIndex,
|
||||||
lastBlock: cp.lastBlock,
|
lastBlock: cp.lastBlock,
|
||||||
blockLvPtrs: cp.blockLvPtrs,
|
blockLvPtrs: slices.Clone(cp.blockLvPtrs),
|
||||||
},
|
},
|
||||||
finishedMaps: make(map[uint32]*renderedMap),
|
finishedMaps: make(map[uint32]*renderedMap),
|
||||||
finished: common.NewRange(cp.mapIndex, 0),
|
finished: common.NewRange(cp.mapIndex, 0),
|
||||||
|
|
@ -143,8 +144,8 @@ func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap {
|
||||||
var best *renderedMap
|
var best *renderedMap
|
||||||
for _, blockNumber := range f.renderSnapshots.Keys() {
|
for _, blockNumber := range f.renderSnapshots.Keys() {
|
||||||
if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() &&
|
if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() &&
|
||||||
blockNumber <= f.indexedView.headNumber && f.indexedView.getBlockId(blockNumber) == cp.lastBlockId &&
|
blockNumber <= f.indexedView.HeadNumber() && f.indexedView.BlockId(blockNumber) == cp.lastBlockId &&
|
||||||
blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId &&
|
blockNumber <= f.targetView.HeadNumber() && f.targetView.BlockId(blockNumber) == cp.lastBlockId &&
|
||||||
cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) {
|
cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) {
|
||||||
best = cp
|
best = cp
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +174,7 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa
|
||||||
return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err)
|
return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err)
|
||||||
}
|
}
|
||||||
if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) ||
|
if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) ||
|
||||||
lastBlock >= f.targetView.headNumber || lastBlockId != f.targetView.getBlockId(lastBlock) {
|
lastBlock >= f.targetView.HeadNumber() || lastBlockId != f.targetView.BlockId(lastBlock) {
|
||||||
continue // map is not full or inconsistent with targetView; roll back
|
continue // map is not full or inconsistent with targetView; roll back
|
||||||
}
|
}
|
||||||
lvPtr, err := f.getBlockLvPointer(lastBlock)
|
lvPtr, err := f.getBlockLvPointer(lastBlock)
|
||||||
|
|
@ -244,10 +245,10 @@ func (f *FilterMaps) loadHeadSnapshot() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.renderSnapshots.Add(f.indexedRange.blocks.Last(), &renderedMap{
|
f.renderSnapshots.Add(f.indexedRange.blocks.Last(), &renderedMap{
|
||||||
filterMap: fm,
|
filterMap: fm.fullCopy(),
|
||||||
mapIndex: f.indexedRange.maps.Last(),
|
mapIndex: f.indexedRange.maps.Last(),
|
||||||
lastBlock: f.indexedRange.blocks.Last(),
|
lastBlock: f.indexedRange.blocks.Last(),
|
||||||
lastBlockId: f.indexedView.getBlockId(f.indexedRange.blocks.Last()),
|
lastBlockId: f.indexedView.BlockId(f.indexedRange.blocks.Last()),
|
||||||
blockLvPtrs: lvPtrs,
|
blockLvPtrs: lvPtrs,
|
||||||
finished: true,
|
finished: true,
|
||||||
headDelimiter: f.indexedRange.headDelimiter,
|
headDelimiter: f.indexedRange.headDelimiter,
|
||||||
|
|
@ -264,7 +265,7 @@ func (r *mapRenderer) makeSnapshot() {
|
||||||
filterMap: r.currentMap.filterMap.fastCopy(),
|
filterMap: r.currentMap.filterMap.fastCopy(),
|
||||||
mapIndex: r.currentMap.mapIndex,
|
mapIndex: r.currentMap.mapIndex,
|
||||||
lastBlock: r.currentMap.lastBlock,
|
lastBlock: r.currentMap.lastBlock,
|
||||||
lastBlockId: r.iterator.chainView.getBlockId(r.currentMap.lastBlock),
|
lastBlockId: r.iterator.chainView.BlockId(r.currentMap.lastBlock),
|
||||||
blockLvPtrs: r.currentMap.blockLvPtrs,
|
blockLvPtrs: r.currentMap.blockLvPtrs,
|
||||||
finished: true,
|
finished: true,
|
||||||
headDelimiter: r.iterator.lvIndex,
|
headDelimiter: r.iterator.lvIndex,
|
||||||
|
|
@ -370,7 +371,7 @@ func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) {
|
||||||
r.currentMap.finished = true
|
r.currentMap.finished = true
|
||||||
r.currentMap.headDelimiter = r.iterator.lvIndex
|
r.currentMap.headDelimiter = r.iterator.lvIndex
|
||||||
}
|
}
|
||||||
r.currentMap.lastBlockId = r.f.targetView.getBlockId(r.currentMap.lastBlock)
|
r.currentMap.lastBlockId = r.f.targetView.BlockId(r.currentMap.lastBlock)
|
||||||
totalTime += time.Since(start)
|
totalTime += time.Since(start)
|
||||||
mapRenderTimer.Update(totalTime)
|
mapRenderTimer.Update(totalTime)
|
||||||
mapLogValueMeter.Mark(logValuesProcessed)
|
mapLogValueMeter.Mark(logValuesProcessed)
|
||||||
|
|
@ -467,15 +468,25 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error {
|
||||||
r.f.filterMapCache.Remove(mapIndex)
|
r.f.filterMapCache.Remove(mapIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var blockNumber uint64
|
||||||
|
if r.finished.First() > 0 {
|
||||||
|
// in order to always ensure continuous block pointers, initialize
|
||||||
|
// blockNumber based on the last block of the previous map, then verify
|
||||||
|
// against the first block associated with each rendered map
|
||||||
|
lastBlock, _, err := r.f.getLastBlockOfMap(r.finished.First() - 1)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get last block of previous map %d: %v", r.finished.First()-1, err)
|
||||||
|
}
|
||||||
|
blockNumber = lastBlock + 1
|
||||||
|
}
|
||||||
// add or update block pointers
|
// add or update block pointers
|
||||||
blockNumber := r.finishedMaps[r.finished.First()].firstBlock()
|
|
||||||
for mapIndex := range r.finished.Iter() {
|
for mapIndex := range r.finished.Iter() {
|
||||||
renderedMap := r.finishedMaps[mapIndex]
|
renderedMap := r.finishedMaps[mapIndex]
|
||||||
|
if blockNumber != renderedMap.firstBlock() {
|
||||||
|
return fmt.Errorf("non-continuous block numbers in rendered map %d (next expected: %d first rendered: %d)", mapIndex, blockNumber, renderedMap.firstBlock())
|
||||||
|
}
|
||||||
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
|
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
|
||||||
checkWriteCnt()
|
checkWriteCnt()
|
||||||
if blockNumber != renderedMap.firstBlock() {
|
|
||||||
panic("non-continuous block numbers")
|
|
||||||
}
|
|
||||||
for _, lvPtr := range renderedMap.blockLvPtrs {
|
for _, lvPtr := range renderedMap.blockLvPtrs {
|
||||||
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
||||||
checkWriteCnt()
|
checkWriteCnt()
|
||||||
|
|
@ -536,6 +547,7 @@ func (r *mapRenderer) getTempRange() (filterMapsRange, error) {
|
||||||
} else {
|
} else {
|
||||||
tempRange.blocks.SetAfterLast(0)
|
tempRange.blocks.SetAfterLast(0)
|
||||||
}
|
}
|
||||||
|
tempRange.headIndexed = false
|
||||||
tempRange.headDelimiter = 0
|
tempRange.headDelimiter = 0
|
||||||
}
|
}
|
||||||
return tempRange, nil
|
return tempRange, nil
|
||||||
|
|
@ -566,8 +578,8 @@ func (r *mapRenderer) getUpdatedRange() (filterMapsRange, error) {
|
||||||
lm := r.finishedMaps[r.finished.Last()]
|
lm := r.finishedMaps[r.finished.Last()]
|
||||||
newRange.headIndexed = lm.finished
|
newRange.headIndexed = lm.finished
|
||||||
if lm.finished {
|
if lm.finished {
|
||||||
newRange.blocks.SetLast(r.f.targetView.headNumber)
|
newRange.blocks.SetLast(r.f.targetView.HeadNumber())
|
||||||
if lm.lastBlock != r.f.targetView.headNumber {
|
if lm.lastBlock != r.f.targetView.HeadNumber() {
|
||||||
panic("map rendering finished but last block != head block")
|
panic("map rendering finished but last block != head block")
|
||||||
}
|
}
|
||||||
newRange.headDelimiter = lm.headDelimiter
|
newRange.headDelimiter = lm.headDelimiter
|
||||||
|
|
@ -665,13 +677,13 @@ var errUnindexedRange = errors.New("unindexed range")
|
||||||
// given block's first log value entry (the block delimiter), according to the
|
// given block's first log value entry (the block delimiter), according to the
|
||||||
// current targetView.
|
// current targetView.
|
||||||
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) {
|
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) {
|
||||||
if blockNumber > f.targetView.headNumber {
|
if blockNumber > f.targetView.HeadNumber() {
|
||||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber)
|
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.HeadNumber())
|
||||||
}
|
}
|
||||||
if !f.indexedRange.blocks.Includes(blockNumber) {
|
if !f.indexedRange.blocks.Includes(blockNumber) {
|
||||||
return nil, errUnindexedRange
|
return nil, errUnindexedRange
|
||||||
}
|
}
|
||||||
finished := blockNumber == f.targetView.headNumber
|
finished := blockNumber == f.targetView.HeadNumber()
|
||||||
l := &logIterator{
|
l := &logIterator{
|
||||||
chainView: f.targetView,
|
chainView: f.targetView,
|
||||||
params: &f.Params,
|
params: &f.Params,
|
||||||
|
|
@ -687,11 +699,11 @@ func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint6
|
||||||
// newLogIteratorFromMapBoundary creates a logIterator starting at the given
|
// newLogIteratorFromMapBoundary creates a logIterator starting at the given
|
||||||
// map boundary, according to the current targetView.
|
// map boundary, according to the current targetView.
|
||||||
func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) {
|
func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) {
|
||||||
if startBlock > f.targetView.headNumber {
|
if startBlock > f.targetView.HeadNumber() {
|
||||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.headNumber)
|
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber())
|
||||||
}
|
}
|
||||||
// get block receipts
|
// get block receipts
|
||||||
receipts := f.targetView.getReceipts(startBlock)
|
receipts := f.targetView.RawReceipts(startBlock)
|
||||||
if receipts == nil {
|
if receipts == nil {
|
||||||
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
||||||
}
|
}
|
||||||
|
|
@ -758,7 +770,7 @@ func (l *logIterator) next() error {
|
||||||
if l.delimiter {
|
if l.delimiter {
|
||||||
l.delimiter = false
|
l.delimiter = false
|
||||||
l.blockNumber++
|
l.blockNumber++
|
||||||
l.receipts = l.chainView.getReceipts(l.blockNumber)
|
l.receipts = l.chainView.RawReceipts(l.blockNumber)
|
||||||
if l.receipts == nil {
|
if l.receipts == nil {
|
||||||
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
|
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
|
||||||
}
|
}
|
||||||
|
|
@ -795,7 +807,7 @@ func (l *logIterator) enforceValidState() {
|
||||||
}
|
}
|
||||||
l.logIndex = 0
|
l.logIndex = 0
|
||||||
}
|
}
|
||||||
if l.blockNumber == l.chainView.headNumber {
|
if l.blockNumber == l.chainView.HeadNumber() {
|
||||||
l.finished = true
|
l.finished = true
|
||||||
} else {
|
} else {
|
||||||
l.delimiter = true
|
l.delimiter = true
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ type MatcherBackend interface {
|
||||||
// all states of the chain since the previous SyncLogIndex or the creation of
|
// all states of the chain since the previous SyncLogIndex or the creation of
|
||||||
// the matcher backend.
|
// the matcher backend.
|
||||||
type SyncRange struct {
|
type SyncRange struct {
|
||||||
HeadNumber uint64
|
IndexedView *ChainView
|
||||||
// block range where the index has not changed since the last matcher sync
|
// block range where the index has not changed since the last matcher sync
|
||||||
// and therefore the set of matches found in this region is guaranteed to
|
// and therefore the set of matches found in this region is guaranteed to
|
||||||
// be valid and complete.
|
// be valid and complete.
|
||||||
|
|
|
||||||
|
|
@ -82,13 +82,26 @@ func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapInde
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockLvPointer returns the starting log value index where the log values
|
// GetBlockLvPointer returns the starting log value index where the log values
|
||||||
// generated by the given block are located. If blockNumber is beyond the current
|
// generated by the given block are located. If blockNumber is beyond the last
|
||||||
// head then the first unoccupied log value index is returned.
|
// indexed block then the pointer will point right after this block, ensuring
|
||||||
|
// that the matcher does not fail and can return a set of results where the
|
||||||
|
// valid range is correct.
|
||||||
// GetBlockLvPointer implements MatcherBackend.
|
// GetBlockLvPointer implements MatcherBackend.
|
||||||
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
||||||
fm.f.indexLock.RLock()
|
fm.f.indexLock.RLock()
|
||||||
defer fm.f.indexLock.RUnlock()
|
defer fm.f.indexLock.RUnlock()
|
||||||
|
|
||||||
|
if blockNumber >= fm.f.indexedRange.blocks.AfterLast() {
|
||||||
|
if fm.f.indexedRange.headIndexed {
|
||||||
|
// return index after head block
|
||||||
|
return fm.f.indexedRange.headDelimiter + 1, nil
|
||||||
|
}
|
||||||
|
if fm.f.indexedRange.blocks.Count() > 0 {
|
||||||
|
// return index at the beginning of the last, partially indexed
|
||||||
|
// block (after the last fully indexed one)
|
||||||
|
blockNumber = fm.f.indexedRange.blocks.Last()
|
||||||
|
}
|
||||||
|
}
|
||||||
return fm.f.getBlockLvPointer(blockNumber)
|
return fm.f.getBlockLvPointer(blockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,24 +124,24 @@ func (fm *FilterMapsMatcherBackend) GetLogByLvIndex(ctx context.Context, lvIndex
|
||||||
// synced signals to the matcher that has triggered a synchronisation that it
|
// synced signals to the matcher that has triggered a synchronisation that it
|
||||||
// has been finished and the log index is consistent with the chain head passed
|
// has been finished and the log index is consistent with the chain head passed
|
||||||
// as a parameter.
|
// as a parameter.
|
||||||
|
//
|
||||||
// Note that if the log index head was far behind the chain head then it might not
|
// Note that if the log index head was far behind the chain head then it might not
|
||||||
// be synced up to the given head in a single step. Still, the latest chain head
|
// be synced up to the given head in a single step. Still, the latest chain head
|
||||||
// should be passed as a parameter and the existing log index should be consistent
|
// should be passed as a parameter and the existing log index should be consistent
|
||||||
// with that chain.
|
// with that chain.
|
||||||
|
//
|
||||||
|
// Note: acquiring the indexLock read lock is unnecessary here, as this function
|
||||||
|
// is always called within the indexLoop.
|
||||||
func (fm *FilterMapsMatcherBackend) synced() {
|
func (fm *FilterMapsMatcherBackend) synced() {
|
||||||
fm.f.indexLock.RLock()
|
|
||||||
fm.f.matchersLock.Lock()
|
fm.f.matchersLock.Lock()
|
||||||
defer func() {
|
defer fm.f.matchersLock.Unlock()
|
||||||
fm.f.matchersLock.Unlock()
|
|
||||||
fm.f.indexLock.RUnlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
indexedBlocks := fm.f.indexedRange.blocks
|
indexedBlocks := fm.f.indexedRange.blocks
|
||||||
if !fm.f.indexedRange.headIndexed && !indexedBlocks.IsEmpty() {
|
if !fm.f.indexedRange.headIndexed && !indexedBlocks.IsEmpty() {
|
||||||
indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block
|
indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block
|
||||||
}
|
}
|
||||||
fm.syncCh <- SyncRange{
|
fm.syncCh <- SyncRange{
|
||||||
HeadNumber: fm.f.targetView.headNumber,
|
IndexedView: fm.f.indexedView,
|
||||||
ValidBlocks: fm.validBlocks,
|
ValidBlocks: fm.validBlocks,
|
||||||
IndexedBlocks: indexedBlocks,
|
IndexedBlocks: indexedBlocks,
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +167,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return SyncRange{}, ctx.Err()
|
return SyncRange{}, ctx.Err()
|
||||||
case <-fm.f.disabledCh:
|
case <-fm.f.disabledCh:
|
||||||
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil
|
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||||
|
// as the indexer has already been terminated.
|
||||||
|
return SyncRange{IndexedView: fm.f.indexedView}, nil
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case vr := <-syncCh:
|
case vr := <-syncCh:
|
||||||
|
|
@ -162,7 +177,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return SyncRange{}, ctx.Err()
|
return SyncRange{}, ctx.Err()
|
||||||
case <-fm.f.disabledCh:
|
case <-fm.f.disabledCh:
|
||||||
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil
|
// Note: acquiring the indexLock read lock is unnecessary here,
|
||||||
|
// as the indexer has already been terminated.
|
||||||
|
return SyncRange{IndexedView: fm.f.indexedView}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,7 +187,9 @@ func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange
|
||||||
// valid range with the current indexed range. This function should be called
|
// valid range with the current indexed range. This function should be called
|
||||||
// whenever a part of the log index has been removed, before adding new blocks
|
// whenever a part of the log index has been removed, before adding new blocks
|
||||||
// to it.
|
// to it.
|
||||||
// Note that this function assumes that the index read lock is being held.
|
//
|
||||||
|
// Note: acquiring the indexLock read lock is unnecessary here, as this function
|
||||||
|
// is always called within the indexLoop.
|
||||||
func (f *FilterMaps) updateMatchersValidRange() {
|
func (f *FilterMaps) updateMatchersValidRange() {
|
||||||
f.matchersLock.Lock()
|
f.matchersLock.Lock()
|
||||||
defer f.matchersLock.Unlock()
|
defer f.matchersLock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||||
return nil, common.Hash{}, nil, errors.New("missing head header")
|
return nil, common.Hash{}, nil, errors.New("missing head header")
|
||||||
}
|
}
|
||||||
newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
|
newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
|
||||||
|
if err := overrides.apply(newCfg); err != nil {
|
||||||
|
return nil, common.Hash{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Sanity-check the new configuration.
|
// Sanity-check the new configuration.
|
||||||
if err := newCfg.CheckConfigForkOrder(); err != nil {
|
if err := newCfg.CheckConfigForkOrder(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -591,17 +591,50 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||||
hashes = append(hashes, hdr.Hash())
|
hashes = append(hashes, hdr.Hash())
|
||||||
}
|
}
|
||||||
for _, hash := range hashes {
|
for _, hash := range hashes {
|
||||||
|
// Remove the associated block body and receipts if required.
|
||||||
|
//
|
||||||
|
// If the block is in the chain freezer, then this delete operation
|
||||||
|
// is actually ineffective.
|
||||||
if delFn != nil {
|
if delFn != nil {
|
||||||
delFn(batch, hash, num)
|
delFn(batch, hash, num)
|
||||||
}
|
}
|
||||||
|
// Remove the hash->number mapping along with the header itself
|
||||||
rawdb.DeleteHeader(batch, hash, num)
|
rawdb.DeleteHeader(batch, hash, num)
|
||||||
}
|
}
|
||||||
|
// Remove the number->hash mapping
|
||||||
rawdb.DeleteCanonicalHash(batch, num)
|
rawdb.DeleteCanonicalHash(batch, num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Flush all accumulated deletions.
|
// Flush all accumulated deletions.
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
log.Crit("Failed to rewind block", "error", err)
|
log.Crit("Failed to commit batch in setHead", "err", err)
|
||||||
|
}
|
||||||
|
// Explicitly flush the pending writes in the key-value store to disk, ensuring
|
||||||
|
// data durability of the previous deletions.
|
||||||
|
if err := hc.chainDb.SyncKeyValue(); err != nil {
|
||||||
|
log.Crit("Failed to sync the key-value store in setHead", "err", err)
|
||||||
|
}
|
||||||
|
// Truncate the excessive chain segments in the ancient store.
|
||||||
|
// These are actually deferred deletions from the loop above.
|
||||||
|
//
|
||||||
|
// This step must be performed after synchronizing the key-value store;
|
||||||
|
// otherwise, in the event of a panic, it's theoretically possible to
|
||||||
|
// lose recent key-value store writes while the ancient store deletions
|
||||||
|
// remain, leading to data inconsistency, e.g., the gap between the key
|
||||||
|
// value store and ancient can be created due to unclean shutdown.
|
||||||
|
if delFn != nil {
|
||||||
|
// Ignore the error here since light client won't hit this path
|
||||||
|
frozen, _ := hc.chainDb.Ancients()
|
||||||
|
header := hc.CurrentHeader()
|
||||||
|
|
||||||
|
// Truncate the excessive chain segment above the current chain head
|
||||||
|
// in the ancient store.
|
||||||
|
if header.Number.Uint64()+1 < frozen {
|
||||||
|
_, err := hc.chainDb.TruncateHead(header.Number.Uint64() + 1)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to truncate head block", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Clear out any stale content from the caches
|
// Clear out any stale content from the caches
|
||||||
hc.headerCache.Purge()
|
hc.headerCache.Purge()
|
||||||
|
|
|
||||||
|
|
@ -545,8 +545,8 @@ func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawVa
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
|
||||||
// The receipt metadata fields are not guaranteed to be populated, so they
|
// The receipt metadata fields and the Bloom are not guaranteed to be populated,
|
||||||
// should not be used. Use ReadReceipts instead if the metadata is needed.
|
// so they should not be used. Use ReadReceipts instead if the metadata is needed.
|
||||||
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
|
||||||
// Retrieve the flattened receipt slice
|
// Retrieve the flattened receipt slice
|
||||||
data := ReadReceiptsRLP(db, hash, number)
|
data := ReadReceiptsRLP(db, hash, number)
|
||||||
|
|
@ -621,6 +621,14 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteRawReceipts stores all the transaction receipts belonging to a block.
|
||||||
|
func WriteRawReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts rlp.RawValue) {
|
||||||
|
// Store the flattened receipt slice
|
||||||
|
if err := db.Put(blockReceiptsKey(number, hash), receipts); err != nil {
|
||||||
|
log.Crit("Failed to store block receipts", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||||
func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||||
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
|
||||||
|
|
@ -701,18 +709,11 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts) (int64, error) {
|
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []rlp.RawValue) (int64, error) {
|
||||||
var stReceipts []*types.ReceiptForStorage
|
|
||||||
|
|
||||||
return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
// Convert receipts to storage format and sum up total difficulty.
|
|
||||||
stReceipts = stReceipts[:0]
|
|
||||||
for _, receipt := range receipts[i] {
|
|
||||||
stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt))
|
|
||||||
}
|
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
if err := writeAncientBlock(op, block, header, stReceipts); err != nil {
|
if err := writeAncientBlock(op, block, header, receipts[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -720,7 +721,7 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage) error {
|
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts rlp.RawValue) error {
|
||||||
num := block.NumberU64()
|
num := block.NumberU64()
|
||||||
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
||||||
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,11 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||||
t.Fatalf("receipts returned when body was deleted: %v", rs)
|
t.Fatalf("receipts returned when body was deleted: %v", rs)
|
||||||
}
|
}
|
||||||
// Ensure that receipts without metadata can be returned without the block body too
|
// Ensure that receipts without metadata can be returned without the block body too
|
||||||
if err := checkReceiptsRLP(ReadRawReceipts(db, hash, 0), receipts); err != nil {
|
raw := ReadRawReceipts(db, hash, 0)
|
||||||
|
for _, r := range raw {
|
||||||
|
r.Bloom = types.CreateBloom(r)
|
||||||
|
}
|
||||||
|
if err := checkReceiptsRLP(raw, receipts); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Sanity check that body alone without the receipt is a full purge
|
// Sanity check that body alone without the receipt is a full purge
|
||||||
|
|
@ -439,7 +443,7 @@ func TestAncientStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write and verify the header in the database
|
// Write and verify the header in the database
|
||||||
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil})
|
WriteAncientBlocks(db, []*types.Block{block}, types.EncodeBlockReceiptLists([]types.Receipts{nil}))
|
||||||
|
|
||||||
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
||||||
t.Fatalf("no header returned")
|
t.Fatalf("no header returned")
|
||||||
|
|
@ -609,7 +613,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||||
|
|
||||||
blocks := allBlocks[i : i+length]
|
blocks := allBlocks[i : i+length]
|
||||||
receipts := batchReceipts[:length]
|
receipts := batchReceipts[:length]
|
||||||
writeSize, err := WriteAncientBlocks(db, blocks, receipts)
|
writeSize, err := WriteAncientBlocks(db, blocks, types.EncodeBlockReceiptLists(receipts))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -909,7 +913,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
receipts := make([]types.Receipts, 100)
|
receipts := make([]types.Receipts, 100)
|
||||||
// Write first half to ancients
|
// Write first half to ancients
|
||||||
WriteAncientBlocks(db, chain[:50], receipts[:50])
|
WriteAncientBlocks(db, chain[:50], types.EncodeBlockReceiptLists(receipts[:50]))
|
||||||
// Write second half to db
|
// Write second half to db
|
||||||
for i := 50; i < 100; i++ {
|
for i := 50; i < 100; i++ {
|
||||||
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
||||||
|
|
|
||||||
|
|
@ -258,13 +258,21 @@ func ReadStateHistory(db ethdb.AncientReaderOp, id uint64) ([]byte, []byte, []by
|
||||||
// WriteStateHistory writes the provided state history to database. Compute the
|
// WriteStateHistory writes the provided state history to database. Compute the
|
||||||
// position of state history in freezer by minus one since the id of first state
|
// position of state history in freezer by minus one since the id of first state
|
||||||
// history starts from one(zero for initial state).
|
// history starts from one(zero for initial state).
|
||||||
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) {
|
func WriteStateHistory(db ethdb.AncientWriter, id uint64, meta []byte, accountIndex []byte, storageIndex []byte, accounts []byte, storages []byte) error {
|
||||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
op.AppendRaw(stateHistoryMeta, id-1, meta)
|
if err := op.AppendRaw(stateHistoryMeta, id-1, meta); err != nil {
|
||||||
op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex)
|
return err
|
||||||
op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex)
|
}
|
||||||
op.AppendRaw(stateHistoryAccountData, id-1, accounts)
|
if err := op.AppendRaw(stateHistoryAccountIndex, id-1, accountIndex); err != nil {
|
||||||
op.AppendRaw(stateHistoryStorageData, id-1, storages)
|
return err
|
||||||
return nil
|
}
|
||||||
|
if err := op.AppendRaw(stateHistoryStorageIndex, id-1, storageIndex); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw(stateHistoryAccountData, id-1, accounts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return op.AppendRaw(stateHistoryStorageData, id-1, storages)
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package rawdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -45,25 +44,6 @@ const HashScheme = "hash"
|
||||||
// on extra state diffs to survive deep reorg.
|
// on extra state diffs to survive deep reorg.
|
||||||
const PathScheme = "path"
|
const PathScheme = "path"
|
||||||
|
|
||||||
// hasher is used to compute the sha256 hash of the provided data.
|
|
||||||
type hasher struct{ sha crypto.KeccakState }
|
|
||||||
|
|
||||||
var hasherPool = sync.Pool{
|
|
||||||
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHasher() *hasher {
|
|
||||||
return hasherPool.Get().(*hasher)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hasher) hash(data []byte) common.Hash {
|
|
||||||
return crypto.HashData(h.sha, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *hasher) release() {
|
|
||||||
hasherPool.Put(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
|
||||||
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
|
||||||
data, _ := db.Get(accountTrieNodeKey(path))
|
data, _ := db.Get(accountTrieNodeKey(path))
|
||||||
|
|
@ -170,9 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
|
||||||
if len(blob) == 0 {
|
if len(blob) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
h := newHasher()
|
return crypto.Keccak256Hash(blob) == hash // exists but not match
|
||||||
defer h.release()
|
|
||||||
return h.hash(blob) == hash // exists but not match
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
panic(fmt.Sprintf("Unknown scheme %v", scheme))
|
||||||
}
|
}
|
||||||
|
|
@ -194,9 +172,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash
|
||||||
if len(blob) == 0 {
|
if len(blob) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
h := newHasher()
|
if crypto.Keccak256Hash(blob) != hash {
|
||||||
defer h.release()
|
|
||||||
if h.hash(blob) != hash {
|
|
||||||
return nil // exists but not match
|
return nil // exists but not match
|
||||||
}
|
}
|
||||||
return blob
|
return blob
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Batch of blocks have been frozen, flush them before wiping from key-value store
|
// Batch of blocks have been frozen, flush them before wiping from key-value store
|
||||||
if err := f.Sync(); err != nil {
|
if err := f.SyncAncient(); err != nil {
|
||||||
log.Crit("Failed to flush frozen tables", "err", err)
|
log.Crit("Failed to flush frozen tables", "err", err)
|
||||||
}
|
}
|
||||||
// Wipe out all data from the active database
|
// Wipe out all data from the active database
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,8 @@ func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
|
||||||
return 0, errNotSupported
|
return 0, errNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync returns an error as we don't have a backing chain freezer.
|
// SyncAncient returns an error as we don't have a backing chain freezer.
|
||||||
func (db *nofreezedb) Sync() error {
|
func (db *nofreezedb) SyncAncient() error {
|
||||||
return errNotSupported
|
return errNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -325,8 +325,8 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
|
||||||
return old, nil
|
return old, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync flushes all data tables to disk.
|
// SyncAncient flushes all data tables to disk.
|
||||||
func (f *Freezer) Sync() error {
|
func (f *Freezer) SyncAncient() error {
|
||||||
var errs []error
|
var errs []error
|
||||||
for _, table := range f.tables {
|
for _, table := range f.tables {
|
||||||
if err := table.Sync(); err != nil {
|
if err := table.Sync(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -395,8 +395,8 @@ func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) {
|
||||||
return old, nil
|
return old, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync flushes all data tables to disk.
|
// SyncAncient flushes all data tables to disk.
|
||||||
func (f *MemoryFreezer) Sync() error {
|
func (f *MemoryFreezer) SyncAncient() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,12 +194,12 @@ func (f *resettableFreezer) TruncateTail(tail uint64) (uint64, error) {
|
||||||
return f.freezer.TruncateTail(tail)
|
return f.freezer.TruncateTail(tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync flushes all data tables to disk.
|
// SyncAncient flushes all data tables to disk.
|
||||||
func (f *resettableFreezer) Sync() error {
|
func (f *resettableFreezer) SyncAncient() error {
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
defer f.lock.RUnlock()
|
defer f.lock.RUnlock()
|
||||||
|
|
||||||
return f.freezer.Sync()
|
return f.freezer.SyncAncient()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AncientDatadir returns the path of the ancient store.
|
// AncientDatadir returns the path of the ancient store.
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@ func TestFreezerCloseSync(t *testing.T) {
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := f.Sync(); err == nil {
|
if err := f.SyncAncient(); err == nil {
|
||||||
t.Fatalf("want error, have nil")
|
t.Fatalf("want error, have nil")
|
||||||
} else if have, want := err.Error(), "[closed closed]"; have != want {
|
} else if have, want := err.Error(), "[closed closed]"; have != want {
|
||||||
t.Fatalf("want %v, have %v", have, want)
|
t.Fatalf("want %v, have %v", have, want)
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,10 @@ func (t *table) TruncateTail(items uint64) (uint64, error) {
|
||||||
return t.db.TruncateTail(items)
|
return t.db.TruncateTail(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync is a noop passthrough that just forwards the request to the underlying
|
// SyncAncient is a noop passthrough that just forwards the request to the underlying
|
||||||
// database.
|
// database.
|
||||||
func (t *table) Sync() error {
|
func (t *table) SyncAncient() error {
|
||||||
return t.db.Sync()
|
return t.db.SyncAncient()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AncientDatadir returns the ancient datadir of the underlying database.
|
// AncientDatadir returns the ancient datadir of the underlying database.
|
||||||
|
|
@ -188,6 +188,12 @@ func (t *table) Compact(start []byte, limit []byte) error {
|
||||||
return t.db.Compact(start, limit)
|
return t.db.Compact(start, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SyncKeyValue ensures that all pending writes are flushed to disk,
|
||||||
|
// guaranteeing data durability up to the point.
|
||||||
|
func (t *table) SyncKeyValue() error {
|
||||||
|
return t.db.SyncKeyValue()
|
||||||
|
}
|
||||||
|
|
||||||
// NewBatch creates a write-only database that buffers changes to its host db
|
// NewBatch creates a write-only database that buffers changes to its host db
|
||||||
// until a final write is called, each operation prefixing all keys with the
|
// until a final write is called, each operation prefixing all keys with the
|
||||||
// pre-configured string.
|
// pre-configured string.
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"maps"
|
"maps"
|
||||||
|
gomath "math"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -92,97 +93,94 @@ func (ae *AccessEvents) Copy() *AccessEvents {
|
||||||
|
|
||||||
// AddAccount returns the gas to be charged for each of the currently cold
|
// AddAccount returns the gas to be charged for each of the currently cold
|
||||||
// member fields of an account.
|
// member fields of an account.
|
||||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
var gas uint64 // accumulate the consumed gas
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
consumed, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
if consumed < expected {
|
||||||
|
return expected
|
||||||
|
}
|
||||||
|
gas += consumed
|
||||||
|
consumed, expected = ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas-consumed)
|
||||||
|
if consumed < expected {
|
||||||
|
return expected + gas
|
||||||
|
}
|
||||||
|
gas += expected
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageCallGas returns the gas to be charged for each of the currently
|
// MessageCallGas returns the gas to be charged for each of the currently
|
||||||
// cold member fields of an account, that need to be touched when making a message
|
// cold member fields of an account, that need to be touched when making a message
|
||||||
// call to that account.
|
// call to that account.
|
||||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
func (ae *AccessEvents) MessageCallGas(destination common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
_, expected := ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
if expected == 0 {
|
||||||
return gas
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueTransferGas returns the gas to be charged for each of the currently
|
// ValueTransferGas returns the gas to be charged for each of the currently
|
||||||
// cold balance member fields of the caller and the callee accounts.
|
// cold balance member fields of the caller and the callee accounts.
|
||||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
_, expected1 := ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
if expected1 > availableGas {
|
||||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
return expected1
|
||||||
return gas
|
}
|
||||||
|
_, expected2 := ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas-expected1)
|
||||||
|
if expected1+expected2 == 0 {
|
||||||
|
return params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected1 + expected2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreatePreCheckGas charges access costs before
|
// ContractCreatePreCheckGas charges access costs before
|
||||||
// a contract creation is initiated. It is just reads, because the
|
// a contract creation is initiated. It is just reads, because the
|
||||||
// address collision is done before the transfer, and so no write
|
// address collision is done before the transfer, and so no write
|
||||||
// are guaranteed to happen at this point.
|
// are guaranteed to happen at this point.
|
||||||
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
|
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address, availableGas uint64) uint64 {
|
||||||
var gas uint64
|
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
_, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false, availableGas-consumed)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
return expected1 + expected2
|
||||||
return gas
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreateInitGas returns the access gas costs for the initialization of
|
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||||
// a contract creation.
|
// a contract creation.
|
||||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, availableGas uint64) (uint64, uint64) {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
consumed, expected1 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true, availableGas)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
|
gas += consumed
|
||||||
return gas
|
consumed, expected2 := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true, availableGas-consumed)
|
||||||
|
gas += consumed
|
||||||
|
return gas, expected1 + expected2
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true, gomath.MaxUint64)
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false, gomath.MaxUint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxDestination adds the member fields of the sender account to the access event list,
|
// AddTxDestination adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue, doesntExist bool) {
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue, gomath.MaxUint64)
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, doesntExist, gomath.MaxUint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||||
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 {
|
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
||||||
return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
|
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the
|
||||||
// access cost to be charged, if need be.
|
// consumed and required gas.
|
||||||
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
|
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||||
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite)
|
|
||||||
|
|
||||||
var gas uint64
|
|
||||||
if stemRead {
|
|
||||||
gas += params.WitnessBranchReadCost
|
|
||||||
}
|
|
||||||
if selectorRead {
|
|
||||||
gas += params.WitnessChunkReadCost
|
|
||||||
}
|
|
||||||
if stemWrite {
|
|
||||||
gas += params.WitnessBranchWriteCost
|
|
||||||
}
|
|
||||||
if selectorWrite {
|
|
||||||
gas += params.WitnessChunkWriteCost
|
|
||||||
}
|
|
||||||
if selectorFill {
|
|
||||||
gas += params.WitnessChunkFillCost
|
|
||||||
}
|
|
||||||
return gas
|
|
||||||
}
|
|
||||||
|
|
||||||
// touchAddress adds any missing access event to the access event list.
|
|
||||||
func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) {
|
|
||||||
branchKey := newBranchAccessKey(addr, treeIndex)
|
branchKey := newBranchAccessKey(addr, treeIndex)
|
||||||
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
||||||
|
|
||||||
|
|
@ -190,11 +188,9 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
||||||
var branchRead, chunkRead bool
|
var branchRead, chunkRead bool
|
||||||
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
||||||
branchRead = true
|
branchRead = true
|
||||||
ae.branches[branchKey] = AccessWitnessReadFlag
|
|
||||||
}
|
}
|
||||||
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
||||||
chunkRead = true
|
chunkRead = true
|
||||||
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write access.
|
// Write access.
|
||||||
|
|
@ -202,17 +198,51 @@ func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int,
|
||||||
if isWrite {
|
if isWrite {
|
||||||
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||||
branchWrite = true
|
branchWrite = true
|
||||||
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkValue := ae.chunks[chunkKey]
|
chunkValue := ae.chunks[chunkKey]
|
||||||
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||||
chunkWrite = true
|
chunkWrite = true
|
||||||
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
|
||||||
}
|
}
|
||||||
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
|
||||||
}
|
}
|
||||||
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
|
|
||||||
|
var gas uint64
|
||||||
|
if branchRead {
|
||||||
|
gas += params.WitnessBranchReadCost
|
||||||
|
}
|
||||||
|
if chunkRead {
|
||||||
|
gas += params.WitnessChunkReadCost
|
||||||
|
}
|
||||||
|
if branchWrite {
|
||||||
|
gas += params.WitnessBranchWriteCost
|
||||||
|
}
|
||||||
|
if chunkWrite {
|
||||||
|
gas += params.WitnessChunkWriteCost
|
||||||
|
}
|
||||||
|
if chunkFill {
|
||||||
|
gas += params.WitnessChunkFillCost
|
||||||
|
}
|
||||||
|
|
||||||
|
if availableGas < gas {
|
||||||
|
// consumed != expected
|
||||||
|
return availableGas, gas
|
||||||
|
}
|
||||||
|
|
||||||
|
if branchRead {
|
||||||
|
ae.branches[branchKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if branchWrite {
|
||||||
|
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
if chunkRead {
|
||||||
|
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if chunkWrite {
|
||||||
|
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// consumed == expected
|
||||||
|
return gas, gas
|
||||||
}
|
}
|
||||||
|
|
||||||
type branchAccessKey struct {
|
type branchAccessKey struct {
|
||||||
|
|
@ -240,7 +270,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
||||||
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool, availableGas uint64) (uint64, uint64) {
|
||||||
// note that in the case where the copied code is outside the range of the
|
// note that in the case where the copied code is outside the range of the
|
||||||
// contract code but touches the last leaf with contract code in it,
|
// contract code but touches the last leaf with contract code in it,
|
||||||
// we don't include the last leaf of code in the AccessWitness. The
|
// we don't include the last leaf of code in the AccessWitness. The
|
||||||
|
|
@ -248,7 +278,7 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
// is already in the AccessWitness so a stateless verifier can see that
|
// is already in the AccessWitness so a stateless verifier can see that
|
||||||
// the code from the last leaf is not needed.
|
// the code from the last leaf is not needed.
|
||||||
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
if (codeLen == 0 && size == 0) || startPC > codeLen {
|
||||||
return 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
endPC := startPC + size
|
endPC := startPC + size
|
||||||
|
|
@ -263,22 +293,34 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
|
||||||
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
|
||||||
subIndex := byte((chunkNumber + 128) % 256)
|
subIndex := byte((chunkNumber + 128) % 256)
|
||||||
gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite)
|
consumed, expected := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite, availableGas)
|
||||||
|
// did we OOG ?
|
||||||
|
if expected > consumed {
|
||||||
|
return statelessGasCharged + consumed, statelessGasCharged + expected
|
||||||
|
}
|
||||||
var overflow bool
|
var overflow bool
|
||||||
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, consumed)
|
||||||
if overflow {
|
if overflow {
|
||||||
panic("overflow when adding gas")
|
panic("overflow when adding gas")
|
||||||
}
|
}
|
||||||
|
availableGas -= consumed
|
||||||
}
|
}
|
||||||
return statelessGasCharged
|
return statelessGasCharged, statelessGasCharged
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
||||||
// amount of gas that it costs.
|
// amount of gas that it costs.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an
|
// Note that an access in write mode implies an access in read mode, whereas an
|
||||||
// access in read mode does not imply an access in write mode.
|
// access in read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
if availableGas < params.WarmStorageReadCostEIP2929 {
|
||||||
|
return availableGas
|
||||||
|
}
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||||
|
|
@ -286,6 +328,13 @@ func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
// read mode does not imply an access in write mode.
|
// read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool, availableGas uint64, chargeWarmCosts bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
_, expected := ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite, availableGas)
|
||||||
|
if expected == 0 && chargeWarmCosts {
|
||||||
|
if availableGas < params.WarmStorageReadCostEIP2929 {
|
||||||
|
return availableGas
|
||||||
|
}
|
||||||
|
expected = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return expected
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -40,50 +41,50 @@ func TestAccountHeaderGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost
|
// Check cold read cost
|
||||||
gas := ae.BasicDataGas(testAddr, false)
|
gas := ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.BasicDataGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read costs in the same group no longer incur the branch read cost
|
// Check cold read costs in the same group no longer incur the branch read cost
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold write cost
|
// Check cold write cost
|
||||||
gas = ae.BasicDataGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm write cost
|
// Check warm write cost
|
||||||
gas = ae.BasicDataGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a write without a read charges both read and write costs
|
// Check a write without a read charges both read and write costs
|
||||||
gas = ae.BasicDataGas(testAddr2, true)
|
gas = ae.BasicDataGas(testAddr2, true, math.MaxUint64, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a write followed by a read charges nothing
|
// Check that a write followed by a read charges nothing
|
||||||
gas = ae.BasicDataGas(testAddr2, false)
|
gas = ae.BasicDataGas(testAddr2, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading a slot from the account header only charges the
|
// Check that reading a slot from the account header only charges the
|
||||||
// chunk read cost.
|
// chunk read cost.
|
||||||
gas = ae.SlotGas(testAddr, common.Hash{}, false)
|
gas = ae.SlotGas(testAddr, common.Hash{}, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
@ -100,13 +101,13 @@ func TestContractCreateInitGas(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.ContractCreateInitGas(testAddr)
|
gas, _ := ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.ContractCreateInitGas(testAddr)
|
gas, _ = ae.ContractCreateInitGas(testAddr, math.MaxUint64)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -118,24 +119,24 @@ func TestMessageCallGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.MessageCallGas(testAddr)
|
gas := ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
||||||
gas = ae.BasicDataGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false, math.MaxUint64, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.MessageCallGas(testAddr)
|
gas = ae.MessageCallGas(testAddr, math.MaxUint64)
|
||||||
if gas != 0 {
|
if gas != params.WarmStorageReadCostEIP2929 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WarmStorageReadCostEIP2929)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Number of codehash->size associations to keep.
|
// Number of codehash->size associations to keep.
|
||||||
codeSizeCacheSize = 100000
|
codeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
||||||
|
|
||||||
// Cache size granted for caching clean code.
|
// Cache size granted for caching clean code.
|
||||||
codeCacheSize = 64 * 1024 * 1024
|
codeCacheSize = 256 * 1024 * 1024
|
||||||
|
|
||||||
// Number of address->curve point associations to keep.
|
// Number of address->curve point associations to keep.
|
||||||
pointCacheSize = 4096
|
pointCacheSize = 4096
|
||||||
|
|
@ -175,26 +175,27 @@ func NewDatabaseForTesting() *CachingDB {
|
||||||
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
var readers []StateReader
|
var readers []StateReader
|
||||||
|
|
||||||
// Set up the state snapshot reader if available. This feature
|
// Configure the state reader using the standalone snapshot in hash mode.
|
||||||
// is optional and may be partially useful if it's not fully
|
// This reader offers improved performance but is optional and only
|
||||||
// generated.
|
// partially useful if the snapshot is not fully generated.
|
||||||
if db.snap != nil {
|
if db.TrieDB().Scheme() == rawdb.HashScheme && db.snap != nil {
|
||||||
// If standalone state snapshot is available (hash scheme),
|
|
||||||
// then construct the legacy snap reader.
|
|
||||||
snap := db.snap.Snapshot(stateRoot)
|
snap := db.snap.Snapshot(stateRoot)
|
||||||
if snap != nil {
|
if snap != nil {
|
||||||
readers = append(readers, newFlatReader(snap))
|
readers = append(readers, newFlatReader(snap))
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// If standalone state snapshot is not available, try to construct
|
// Configure the state reader using the path database in path mode.
|
||||||
// the state reader with database.
|
// This reader offers improved performance but is optional and only
|
||||||
|
// partially useful if the snapshot data in path database is not
|
||||||
|
// fully generated.
|
||||||
|
if db.TrieDB().Scheme() == rawdb.PathScheme {
|
||||||
reader, err := db.triedb.StateReader(stateRoot)
|
reader, err := db.triedb.StateReader(stateRoot)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
readers = append(readers, newFlatReader(reader)) // state reader is optional
|
readers = append(readers, newFlatReader(reader))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set up the trie reader, which is expected to always be available
|
// Configure the trie reader, which is expected to be available as the
|
||||||
// as the gatekeeper unless the state is corrupted.
|
// gatekeeper unless the state is corrupted.
|
||||||
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -208,6 +209,15 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReaderWithCache creates a state reader with internal local cache.
|
||||||
|
func (db *CachingDB) ReaderWithCache(stateRoot common.Hash) (Reader, error) {
|
||||||
|
reader, err := db.Reader(stateRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newReaderWithCache(reader), nil
|
||||||
|
}
|
||||||
|
|
||||||
// OpenTrie opens the main account trie at a specific root hash.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
if db.triedb.IsVerkle() {
|
if db.triedb.IsVerkle() {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
|
|
@ -51,6 +52,9 @@ type ContractCodeReader interface {
|
||||||
|
|
||||||
// StateReader defines the interface for accessing accounts and storage slots
|
// StateReader defines the interface for accessing accounts and storage slots
|
||||||
// associated with a specific state.
|
// associated with a specific state.
|
||||||
|
//
|
||||||
|
// StateReader is assumed to be thread-safe and implementation must take care
|
||||||
|
// of the concurrency issue by themselves.
|
||||||
type StateReader interface {
|
type StateReader interface {
|
||||||
// Account retrieves the account associated with a particular address.
|
// Account retrieves the account associated with a particular address.
|
||||||
//
|
//
|
||||||
|
|
@ -70,6 +74,9 @@ type StateReader interface {
|
||||||
|
|
||||||
// Reader defines the interface for accessing accounts, storage slots and contract
|
// Reader defines the interface for accessing accounts, storage slots and contract
|
||||||
// code associated with a specific state.
|
// code associated with a specific state.
|
||||||
|
//
|
||||||
|
// Reader is assumed to be thread-safe and implementation must take care of the
|
||||||
|
// concurrency issue by themselves.
|
||||||
type Reader interface {
|
type Reader interface {
|
||||||
ContractCodeReader
|
ContractCodeReader
|
||||||
StateReader
|
StateReader
|
||||||
|
|
@ -77,6 +84,8 @@ type Reader interface {
|
||||||
|
|
||||||
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
||||||
// local key-value store or the shared code cache.
|
// local key-value store or the shared code cache.
|
||||||
|
//
|
||||||
|
// cachingCodeReader is safe for concurrent access.
|
||||||
type cachingCodeReader struct {
|
type cachingCodeReader struct {
|
||||||
db ethdb.KeyValueReader
|
db ethdb.KeyValueReader
|
||||||
|
|
||||||
|
|
@ -123,18 +132,14 @@ func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash)
|
||||||
return len(code), nil
|
return len(code), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// flatReader wraps a database state reader.
|
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||||
type flatReader struct {
|
type flatReader struct {
|
||||||
reader database.StateReader
|
reader database.StateReader
|
||||||
buff crypto.KeccakState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFlatReader constructs a state reader with on the given state root.
|
// newFlatReader constructs a state reader with on the given state root.
|
||||||
func newFlatReader(reader database.StateReader) *flatReader {
|
func newFlatReader(reader database.StateReader) *flatReader {
|
||||||
return &flatReader{
|
return &flatReader{reader: reader}
|
||||||
reader: reader,
|
|
||||||
buff: crypto.NewKeccakState(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account implements StateReader, retrieving the account specified by the address.
|
// Account implements StateReader, retrieving the account specified by the address.
|
||||||
|
|
@ -144,7 +149,7 @@ func newFlatReader(reader database.StateReader) *flatReader {
|
||||||
//
|
//
|
||||||
// The returned account might be nil if it's not existent.
|
// The returned account might be nil if it's not existent.
|
||||||
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes()))
|
account, err := r.reader.Account(crypto.Keccak256Hash(addr.Bytes()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -174,8 +179,8 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
//
|
//
|
||||||
// The returned storage slot might be empty if it's not existent.
|
// The returned storage slot might be empty if it's not existent.
|
||||||
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||||
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||||
slotHash := crypto.HashData(r.buff, key.Bytes())
|
slotHash := crypto.Keccak256Hash(key.Bytes())
|
||||||
ret, err := r.reader.Storage(addrHash, slotHash)
|
ret, err := r.reader.Storage(addrHash, slotHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
|
|
@ -196,13 +201,20 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
|
|
||||||
// trieReader implements the StateReader interface, providing functions to access
|
// trieReader implements the StateReader interface, providing functions to access
|
||||||
// state from the referenced trie.
|
// state from the referenced trie.
|
||||||
|
//
|
||||||
|
// trieReader is safe for concurrent read.
|
||||||
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
|
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||||
mainTrie Trie // Main trie, resolved in constructor
|
|
||||||
|
// Main trie, resolved in constructor. Note either the Merkle-Patricia-tree
|
||||||
|
// or Verkle-tree is not safe for concurrent read.
|
||||||
|
mainTrie Trie
|
||||||
|
|
||||||
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
||||||
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
||||||
|
lock sync.Mutex // Lock for protecting concurrent read
|
||||||
}
|
}
|
||||||
|
|
||||||
// trieReader constructs a trie reader of the specific state. An error will be
|
// trieReader constructs a trie reader of the specific state. An error will be
|
||||||
|
|
@ -230,11 +242,8 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account implements StateReader, retrieving the account specified by the address.
|
// account is the inner version of Account and assumes the r.lock is already held.
|
||||||
//
|
func (r *trieReader) account(addr common.Address) (*types.StateAccount, error) {
|
||||||
// An error will be returned if the trie state is corrupted. An nil account
|
|
||||||
// will be returned if it's not existent in the trie.
|
|
||||||
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|
||||||
account, err := r.mainTrie.GetAccount(addr)
|
account, err := r.mainTrie.GetAccount(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -247,12 +256,26 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Account implements StateReader, retrieving the account specified by the address.
|
||||||
|
//
|
||||||
|
// An error will be returned if the trie state is corrupted. An nil account
|
||||||
|
// will be returned if it's not existent in the trie.
|
||||||
|
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
return r.account(addr)
|
||||||
|
}
|
||||||
|
|
||||||
// Storage implements StateReader, retrieving the storage slot specified by the
|
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||||
// address and slot key.
|
// address and slot key.
|
||||||
//
|
//
|
||||||
// An error will be returned if the trie state is corrupted. An empty storage
|
// An error will be returned if the trie state is corrupted. An empty storage
|
||||||
// slot will be returned if it's not existent in the trie.
|
// slot will be returned if it's not existent in the trie.
|
||||||
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tr Trie
|
tr Trie
|
||||||
found bool
|
found bool
|
||||||
|
|
@ -268,7 +291,7 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
// The storage slot is accessed without account caching. It's unexpected
|
// The storage slot is accessed without account caching. It's unexpected
|
||||||
// behavior but try to resolve the account first anyway.
|
// behavior but try to resolve the account first anyway.
|
||||||
if !ok {
|
if !ok {
|
||||||
_, err := r.Account(addr)
|
_, err := r.account(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -293,6 +316,9 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
|
||||||
// multiStateReader is the aggregation of a list of StateReader interface,
|
// multiStateReader is the aggregation of a list of StateReader interface,
|
||||||
// providing state access by leveraging all readers. The checking priority
|
// providing state access by leveraging all readers. The checking priority
|
||||||
// is determined by the position in the reader list.
|
// is determined by the position in the reader list.
|
||||||
|
//
|
||||||
|
// multiStateReader is safe for concurrent read and assumes all underlying
|
||||||
|
// readers are thread-safe as well.
|
||||||
type multiStateReader struct {
|
type multiStateReader struct {
|
||||||
readers []StateReader // List of state readers, sorted by checking priority
|
readers []StateReader // List of state readers, sorted by checking priority
|
||||||
}
|
}
|
||||||
|
|
@ -358,3 +384,95 @@ func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
|
||||||
StateReader: stateReader,
|
StateReader: stateReader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readerWithCache is a wrapper around Reader that maintains additional state caches
|
||||||
|
// to support concurrent state access.
|
||||||
|
type readerWithCache struct {
|
||||||
|
Reader // safe for concurrent read
|
||||||
|
|
||||||
|
// Previously resolved state entries.
|
||||||
|
accounts map[common.Address]*types.StateAccount
|
||||||
|
accountLock sync.RWMutex
|
||||||
|
|
||||||
|
// List of storage buckets, each of which is thread-safe.
|
||||||
|
// This reader is typically used in scenarios requiring concurrent
|
||||||
|
// access to storage. Using multiple buckets helps mitigate
|
||||||
|
// the overhead caused by locking.
|
||||||
|
storageBuckets [16]struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
storages map[common.Address]map[common.Hash]common.Hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newReaderWithCache constructs the reader with local cache.
|
||||||
|
func newReaderWithCache(reader Reader) *readerWithCache {
|
||||||
|
r := &readerWithCache{
|
||||||
|
Reader: reader,
|
||||||
|
accounts: make(map[common.Address]*types.StateAccount),
|
||||||
|
}
|
||||||
|
for i := range r.storageBuckets {
|
||||||
|
r.storageBuckets[i].storages = make(map[common.Address]map[common.Hash]common.Hash)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account implements StateReader, retrieving the account specified by the address.
|
||||||
|
// The returned account might be nil if it's not existent.
|
||||||
|
//
|
||||||
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
|
func (r *readerWithCache) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
// Try to resolve the requested account in the local cache
|
||||||
|
r.accountLock.RLock()
|
||||||
|
acct, ok := r.accounts[addr]
|
||||||
|
r.accountLock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
// Try to resolve the requested account from the underlying reader
|
||||||
|
acct, err := r.Reader.Account(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.accountLock.Lock()
|
||||||
|
r.accounts[addr] = acct
|
||||||
|
r.accountLock.Unlock()
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||||
|
// address and slot key. The returned storage slot might be empty if it's not
|
||||||
|
// existent.
|
||||||
|
//
|
||||||
|
// An error will be returned if the state is corrupted in the underlying reader.
|
||||||
|
func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
|
var (
|
||||||
|
value common.Hash
|
||||||
|
ok bool
|
||||||
|
bucket = &r.storageBuckets[addr[0]&0x0f]
|
||||||
|
)
|
||||||
|
// Try to resolve the requested storage slot in the local cache
|
||||||
|
bucket.lock.RLock()
|
||||||
|
slots, ok := bucket.storages[addr]
|
||||||
|
if ok {
|
||||||
|
value, ok = slots[slot]
|
||||||
|
}
|
||||||
|
bucket.lock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
// Try to resolve the requested storage slot from the underlying reader
|
||||||
|
value, err := r.Reader.Storage(addr, slot)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
bucket.lock.Lock()
|
||||||
|
slots, ok = bucket.storages[addr]
|
||||||
|
if !ok {
|
||||||
|
slots = make(map[common.Hash]common.Hash)
|
||||||
|
bucket.storages[addr] = slots
|
||||||
|
}
|
||||||
|
slots[slot] = value
|
||||||
|
bucket.lock.Unlock()
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,9 @@ func newHelper(scheme string) *testHelper {
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
config := &triedb.Config{}
|
config := &triedb.Config{}
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
config.PathDB = &pathdb.Config{} // disable caching
|
config.PathDB = &pathdb.Config{
|
||||||
|
SnapshotNoBuild: true,
|
||||||
|
} // disable caching
|
||||||
} else {
|
} else {
|
||||||
config.HashDB = &hashdb.Config{} // disable caching
|
config.HashDB = &hashdb.Config{} // disable caching
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,17 @@ type StateDB struct {
|
||||||
|
|
||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
func New(root common.Hash, db Database) (*StateDB, error) {
|
func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
tr, err := db.OpenTrie(root)
|
reader, err := db.Reader(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reader, err := db.Reader(root)
|
return NewWithReader(root, db, reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithReader creates a new state for the specified state root. Unlike New,
|
||||||
|
// this function accepts an additional Reader which is bound to the given root.
|
||||||
|
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
|
||||||
|
tr, err := db.OpenTrie(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +298,7 @@ func (s *StateDB) SubRefund(gas uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exist reports whether the given account address exists in the state.
|
// Exist reports whether the given account address exists in the state.
|
||||||
// Notably this also returns true for self-destructed accounts.
|
// Notably this also returns true for self-destructed accounts within the current transaction.
|
||||||
func (s *StateDB) Exist(addr common.Address) bool {
|
func (s *StateDB) Exist(addr common.Address) bool {
|
||||||
return s.getStateObject(addr) != nil
|
return s.getStateObject(addr) != nil
|
||||||
}
|
}
|
||||||
|
|
@ -392,6 +398,12 @@ func (s *StateDB) Database() Database {
|
||||||
return s.db
|
return s.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reader retrieves the low level database reader supporting the
|
||||||
|
// lower level operations.
|
||||||
|
func (s *StateDB) Reader() Reader {
|
||||||
|
return s.reader
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
|
@ -650,11 +662,10 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
||||||
// Snapshots of the copied state cannot be applied to the copy.
|
// Snapshots of the copied state cannot be applied to the copy.
|
||||||
func (s *StateDB) Copy() *StateDB {
|
func (s *StateDB) Copy() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Copy all the basic fields, initialize the memory ones
|
||||||
reader, _ := s.db.Reader(s.originalRoot) // impossible to fail
|
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
trie: mustCopyTrie(s.trie),
|
trie: mustCopyTrie(s.trie),
|
||||||
reader: reader,
|
reader: s.reader,
|
||||||
originalRoot: s.originalRoot,
|
originalRoot: s.originalRoot,
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
||||||
|
|
|
||||||
|
|
@ -979,7 +979,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
|
||||||
)
|
)
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
|
tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
|
||||||
CleanCacheSize: 0,
|
TrieCleanSize: 0,
|
||||||
|
StateCleanSize: 0,
|
||||||
WriteBufferSize: 0,
|
WriteBufferSize: 0,
|
||||||
}}) // disable caching
|
}}) // disable caching
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,22 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"runtime"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// statePrefetcher is a basic Prefetcher, which blindly executes a block on top
|
// statePrefetcher is a basic Prefetcher that executes transactions from a block
|
||||||
// of an arbitrary state with the goal of prefetching potentially useful state
|
// on top of the parent state, aiming to prefetch potentially useful state data
|
||||||
// data from disk before the main block processor start executing.
|
// from disk. Transactions are executed in parallel to fully leverage the
|
||||||
|
// SSD's read performance.
|
||||||
type statePrefetcher struct {
|
type statePrefetcher struct {
|
||||||
config *params.ChainConfig // Chain configuration options
|
config *params.ChainConfig // Chain configuration options
|
||||||
chain *HeaderChain // Canonical block chain
|
chain *HeaderChain // Canonical block chain
|
||||||
|
|
@ -43,41 +48,81 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr
|
||||||
|
|
||||||
// Prefetch processes the state changes according to the Ethereum rules by running
|
// Prefetch processes the state changes according to the Ethereum rules by running
|
||||||
// the transaction messages using the statedb, but any changes are discarded. The
|
// the transaction messages using the statedb, but any changes are discarded. The
|
||||||
// only goal is to pre-cache transaction signatures and state trie nodes.
|
// only goal is to warm the state caches.
|
||||||
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) {
|
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) {
|
||||||
var (
|
var (
|
||||||
header = block.Header()
|
fails atomic.Int64
|
||||||
gaspool = new(GasPool).AddGas(block.GasLimit())
|
header = block.Header()
|
||||||
blockContext = NewEVMBlockContext(header, p.chain, nil)
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
evm = vm.NewEVM(blockContext, statedb, p.config, cfg)
|
workers errgroup.Group
|
||||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
reader = statedb.Reader()
|
||||||
)
|
)
|
||||||
// Iterate over and process the individual transactions
|
workers.SetLimit(runtime.NumCPU() / 2)
|
||||||
byzantium := p.config.IsByzantium(block.Number())
|
|
||||||
for i, tx := range block.Transactions() {
|
|
||||||
// If block precaching was interrupted, abort
|
|
||||||
if interrupt != nil && interrupt.Load() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Convert the transaction into an executable message and pre-cache its sender
|
|
||||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
|
||||||
if err != nil {
|
|
||||||
return // Also invalid block, bail out
|
|
||||||
}
|
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
|
||||||
|
|
||||||
// We attempt to apply a transaction. The goal is not to execute
|
// Iterate over and process the individual transactions
|
||||||
// the transaction successfully, rather to warm up touched data slots.
|
for i, tx := range block.Transactions() {
|
||||||
if _, err := ApplyMessage(evm, msg, gaspool); err != nil {
|
stateCpy := statedb.Copy() // closure
|
||||||
return // Ugh, something went horribly wrong, bail out
|
workers.Go(func() error {
|
||||||
}
|
// If block precaching was interrupted, abort
|
||||||
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
|
if interrupt != nil && interrupt.Load() {
|
||||||
if !byzantium {
|
return nil
|
||||||
statedb.IntermediateRoot(true)
|
}
|
||||||
}
|
// Preload the touched accounts and storage slots in advance
|
||||||
}
|
sender, err := types.Sender(signer, tx)
|
||||||
// If were post-byzantium, pre-load trie nodes for the final root hash
|
if err != nil {
|
||||||
if byzantium {
|
fails.Add(1)
|
||||||
statedb.IntermediateRoot(true)
|
return nil
|
||||||
|
}
|
||||||
|
reader.Account(sender)
|
||||||
|
|
||||||
|
if tx.To() != nil {
|
||||||
|
account, _ := reader.Account(*tx.To())
|
||||||
|
|
||||||
|
// Preload the contract code if the destination has non-empty code
|
||||||
|
if account != nil && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||||
|
reader.Code(*tx.To(), common.BytesToHash(account.CodeHash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, list := range tx.AccessList() {
|
||||||
|
reader.Account(list.Address)
|
||||||
|
if len(list.StorageKeys) > 0 {
|
||||||
|
for _, slot := range list.StorageKeys {
|
||||||
|
reader.Storage(list.Address, slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Execute the message to preload the implicit touched states
|
||||||
|
evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg)
|
||||||
|
|
||||||
|
// Convert the transaction into an executable message and pre-cache its sender
|
||||||
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
fails.Add(1)
|
||||||
|
return nil // Also invalid block, bail out
|
||||||
|
}
|
||||||
|
// Disable the nonce check
|
||||||
|
msg.SkipNonceChecks = true
|
||||||
|
|
||||||
|
stateCpy.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
|
// We attempt to apply a transaction. The goal is not to execute
|
||||||
|
// the transaction successfully, rather to warm up touched data slots.
|
||||||
|
if _, err := ApplyMessage(evm, msg, new(GasPool).AddGas(block.GasLimit())); err != nil {
|
||||||
|
fails.Add(1)
|
||||||
|
return nil // Ugh, something went horribly wrong, bail out
|
||||||
|
}
|
||||||
|
// Pre-load trie nodes for the intermediate root.
|
||||||
|
//
|
||||||
|
// This operation incurs significant memory allocations due to
|
||||||
|
// trie hashing and node decoding. TODO(rjl493456442): investigate
|
||||||
|
// ways to mitigate this overhead.
|
||||||
|
stateCpy.IntermediateRoot(true)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
workers.Wait()
|
||||||
|
|
||||||
|
blockPrefetchTxsValidMeter.Mark(int64(len(block.Transactions())) - fails.Load())
|
||||||
|
blockPrefetchTxsInvalidMeter.Mark(fails.Load())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,9 +113,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
ProcessWithdrawalQueue(&requests, evm)
|
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
ProcessConsolidationQueue(&requests, evm)
|
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
|
|
@ -265,17 +269,17 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
||||||
|
|
||||||
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
||||||
// It returns the opaque request data returned by the contract.
|
// It returns the opaque request data returned by the contract.
|
||||||
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) {
|
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) error {
|
||||||
processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
|
return processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
||||||
// It returns the opaque request data returned by the contract.
|
// It returns the opaque request data returned by the contract.
|
||||||
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) {
|
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) error {
|
||||||
processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
|
return processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) {
|
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) error {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -292,17 +296,20 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
||||||
}
|
}
|
||||||
evm.SetTxContext(NewEVMTxContext(msg))
|
evm.SetTxContext(NewEVMTxContext(msg))
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
ret, _, _ := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
evm.StateDB.Finalise(true)
|
evm.StateDB.Finalise(true)
|
||||||
if len(ret) == 0 {
|
if err != nil {
|
||||||
return // skip empty output
|
return fmt.Errorf("system call failed to execute: %v", err)
|
||||||
|
}
|
||||||
|
if len(ret) == 0 {
|
||||||
|
return nil // skip empty output
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append prefixed requestsData to the requests list.
|
// Append prefixed requestsData to the requests list.
|
||||||
requestsData := make([]byte, len(ret)+1)
|
requestsData := make([]byte, len(ret)+1)
|
||||||
requestsData[0] = requestType
|
requestsData[0] = requestType
|
||||||
copy(requestsData[1:], ret)
|
copy(requestsData[1:], ret)
|
||||||
*requests = append(*requests, requestsData)
|
*requests = append(*requests, requestsData)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,8 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
|
want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)",
|
||||||
},
|
},
|
||||||
// ErrSetCodeTxCreate cannot be tested: it is impossible to create a SetCode-tx with nil `to`.
|
// ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`.
|
||||||
|
// The EstimateGas API tests test this case.
|
||||||
} {
|
} {
|
||||||
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false)
|
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false)
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ import (
|
||||||
// ExecutionResult includes all output after executing given evm
|
// ExecutionResult includes all output after executing given evm
|
||||||
// message no matter the execution itself is successful or not.
|
// message no matter the execution itself is successful or not.
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
UsedGas uint64 // Total used gas, not including the refunded gas
|
UsedGas uint64 // Total used gas, not including the refunded gas
|
||||||
RefundedGas uint64 // Total gas refunded after execution
|
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
|
||||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||||
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap returns the internal evm error which allows us for further
|
// Unwrap returns the internal evm error which allows us for further
|
||||||
|
|
@ -159,7 +159,9 @@ type Message struct {
|
||||||
|
|
||||||
// When SkipNonceChecks is true, the message nonce is not checked against the
|
// When SkipNonceChecks is true, the message nonce is not checked against the
|
||||||
// account nonce in state.
|
// account nonce in state.
|
||||||
// This field will be set to true for operations like RPC eth_call.
|
//
|
||||||
|
// This field will be set to true for operations like RPC eth_call
|
||||||
|
// or the state prefetching.
|
||||||
SkipNonceChecks bool
|
SkipNonceChecks bool
|
||||||
|
|
||||||
// When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
|
// When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
|
||||||
|
|
@ -455,7 +457,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||||
|
|
||||||
if targetAddr := msg.To; targetAddr != nil {
|
if targetAddr := msg.To; targetAddr != nil {
|
||||||
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0)
|
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0, !st.state.Exist(*targetAddr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,9 +511,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record the gas used excluding gas refunds. This value represents the actual
|
||||||
|
// gas allowance required to complete execution.
|
||||||
|
peakGasUsed := st.gasUsed()
|
||||||
|
|
||||||
// Compute refund counter, capped to a refund quotient.
|
// Compute refund counter, capped to a refund quotient.
|
||||||
gasRefund := st.calcRefund()
|
st.gasRemaining += st.calcRefund()
|
||||||
st.gasRemaining += gasRefund
|
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||||
if st.gasUsed() < floorDataGas {
|
if st.gasUsed() < floorDataGas {
|
||||||
|
|
@ -521,6 +526,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if peakGasUsed < floorDataGas {
|
||||||
|
peakGasUsed = floorDataGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
st.returnGas()
|
st.returnGas()
|
||||||
|
|
||||||
|
|
@ -544,15 +552,15 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
|
|
||||||
// add the coinbase to the witness iff the fee is greater than 0
|
// add the coinbase to the witness iff the fee is greater than 0
|
||||||
if rules.IsEIP4762 && fee.Sign() != 0 {
|
if rules.IsEIP4762 && fee.Sign() != 0 {
|
||||||
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true)
|
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true, math.MaxUint64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
UsedGas: st.gasUsed(),
|
UsedGas: st.gasUsed(),
|
||||||
RefundedGas: gasRefund,
|
MaxUsedGas: peakGasUsed,
|
||||||
Err: vmerr,
|
Err: vmerr,
|
||||||
ReturnData: ret,
|
ReturnData: ret,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
|
@ -47,26 +47,38 @@ type txIndexer struct {
|
||||||
// and all others shouldn't.
|
// and all others shouldn't.
|
||||||
limit uint64
|
limit uint64
|
||||||
|
|
||||||
|
// The current head of blockchain for transaction indexing. This field
|
||||||
|
// is accessed by both the indexer and the indexing progress queries.
|
||||||
|
head atomic.Uint64
|
||||||
|
|
||||||
|
// The current tail of the indexed transactions, null indicates
|
||||||
|
// that no transactions have been indexed yet.
|
||||||
|
//
|
||||||
|
// This field is accessed by both the indexer and the indexing
|
||||||
|
// progress queries.
|
||||||
|
tail atomic.Pointer[uint64]
|
||||||
|
|
||||||
// cutoff denotes the block number before which the chain segment should
|
// cutoff denotes the block number before which the chain segment should
|
||||||
// be pruned and not available locally.
|
// be pruned and not available locally.
|
||||||
cutoff uint64
|
cutoff uint64
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
progress chan chan TxIndexProgress
|
term chan chan struct{}
|
||||||
term chan chan struct{}
|
closed chan struct{}
|
||||||
closed chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxIndexer initializes the transaction indexer.
|
// newTxIndexer initializes the transaction indexer.
|
||||||
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
||||||
cutoff, _ := chain.HistoryPruningCutoff()
|
cutoff, _ := chain.HistoryPruningCutoff()
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: limit,
|
limit: limit,
|
||||||
cutoff: cutoff,
|
cutoff: cutoff,
|
||||||
db: chain.db,
|
db: chain.db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
term: make(chan chan struct{}),
|
||||||
term: make(chan chan struct{}),
|
closed: make(chan struct{}),
|
||||||
closed: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
indexer.head.Store(indexer.resolveHead())
|
||||||
|
indexer.tail.Store(rawdb.ReadTxIndexTail(chain.db))
|
||||||
|
|
||||||
go indexer.loop(chain)
|
go indexer.loop(chain)
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
@ -154,6 +166,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// A crash may occur between the two delete operations,
|
// A crash may occur between the two delete operations,
|
||||||
// potentially leaving dangling indexes in the database.
|
// potentially leaving dangling indexes in the database.
|
||||||
// However, this is considered acceptable.
|
// However, this is considered acceptable.
|
||||||
|
indexer.tail.Store(nil)
|
||||||
rawdb.DeleteTxIndexTail(indexer.db)
|
rawdb.DeleteTxIndexTail(indexer.db)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||||
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
||||||
|
|
@ -174,6 +187,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// Traversing the database directly within the transaction
|
// Traversing the database directly within the transaction
|
||||||
// index namespace might be slow and expensive, but we
|
// index namespace might be slow and expensive, but we
|
||||||
// have no choice.
|
// have no choice.
|
||||||
|
indexer.tail.Store(nil)
|
||||||
rawdb.DeleteTxIndexTail(indexer.db)
|
rawdb.DeleteTxIndexTail(indexer.db)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||||
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
||||||
|
|
@ -187,6 +201,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// A crash may occur between the two delete operations,
|
// A crash may occur between the two delete operations,
|
||||||
// potentially leaving dangling indexes in the database.
|
// potentially leaving dangling indexes in the database.
|
||||||
// However, this is considered acceptable.
|
// However, this is considered acceptable.
|
||||||
|
indexer.tail.Store(&indexer.cutoff)
|
||||||
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
||||||
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
||||||
|
|
@ -216,16 +231,15 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
|
|
||||||
// Listening to chain events and manipulate the transaction indexes.
|
// Listening to chain events and manipulate the transaction indexes.
|
||||||
var (
|
var (
|
||||||
stop chan struct{} // Non-nil if background routine is active
|
stop chan struct{} // Non-nil if background routine is active
|
||||||
done chan struct{} // Non-nil if background routine is active
|
done chan struct{} // Non-nil if background routine is active
|
||||||
head = indexer.resolveHead() // The latest announced chain head
|
|
||||||
|
|
||||||
headCh = make(chan ChainHeadEvent)
|
headCh = make(chan ChainHeadEvent)
|
||||||
sub = chain.SubscribeChainHeadEvent(headCh)
|
sub = chain.SubscribeChainHeadEvent(headCh)
|
||||||
)
|
)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
// Validate the transaction indexes and repair if necessary
|
// Validate the transaction indexes and repair if necessary
|
||||||
|
head := indexer.head.Load()
|
||||||
indexer.repair(head)
|
indexer.repair(head)
|
||||||
|
|
||||||
// Launch the initial processing if chain is not empty (head != genesis).
|
// Launch the initial processing if chain is not empty (head != genesis).
|
||||||
|
|
@ -238,17 +252,18 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case h := <-headCh:
|
case h := <-headCh:
|
||||||
|
indexer.head.Store(h.Header.Number.Uint64())
|
||||||
if done == nil {
|
if done == nil {
|
||||||
stop = make(chan struct{})
|
stop = make(chan struct{})
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
||||||
}
|
}
|
||||||
head = h.Header.Number.Uint64()
|
|
||||||
case <-done:
|
case <-done:
|
||||||
stop = nil
|
stop = nil
|
||||||
done = nil
|
done = nil
|
||||||
case ch := <-indexer.progress:
|
indexer.tail.Store(rawdb.ReadTxIndexTail(indexer.db))
|
||||||
ch <- indexer.report(head)
|
|
||||||
case ch := <-indexer.term:
|
case ch := <-indexer.term:
|
||||||
if stop != nil {
|
if stop != nil {
|
||||||
close(stop)
|
close(stop)
|
||||||
|
|
@ -264,7 +279,7 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// report returns the tx indexing progress.
|
// report returns the tx indexing progress.
|
||||||
func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress {
|
||||||
// Special case if the head is even below the cutoff,
|
// Special case if the head is even below the cutoff,
|
||||||
// nothing to index.
|
// nothing to index.
|
||||||
if head < indexer.cutoff {
|
if head < indexer.cutoff {
|
||||||
|
|
@ -284,7 +299,6 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||||
}
|
}
|
||||||
// Compute how many blocks have been indexed
|
// Compute how many blocks have been indexed
|
||||||
var indexed uint64
|
var indexed uint64
|
||||||
tail := rawdb.ReadTxIndexTail(indexer.db)
|
|
||||||
if tail != nil {
|
if tail != nil {
|
||||||
indexed = head - *tail + 1
|
indexed = head - *tail + 1
|
||||||
}
|
}
|
||||||
|
|
@ -300,16 +314,12 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// txIndexProgress retrieves the tx indexing progress, or an error if the
|
// txIndexProgress retrieves the transaction indexing progress. The reported
|
||||||
// background tx indexer is already stopped.
|
// progress may slightly lag behind the actual indexing state, as the tail is
|
||||||
func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) {
|
// only updated at the end of each indexing operation. However, this delay is
|
||||||
ch := make(chan TxIndexProgress, 1)
|
// considered acceptable.
|
||||||
select {
|
func (indexer *txIndexer) txIndexProgress() TxIndexProgress {
|
||||||
case indexer.progress <- ch:
|
return indexer.report(indexer.head.Load(), indexer.tail.Load())
|
||||||
return <-ch, nil
|
|
||||||
case <-indexer.closed:
|
|
||||||
return TxIndexProgress{}, errors.New("indexer is closed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// close shutdown the indexer. Safe to be called for multiple times.
|
// close shutdown the indexer. Safe to be called for multiple times.
|
||||||
|
|
|
||||||
|
|
@ -117,13 +117,12 @@ func TestTxIndexer(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...)))
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: 0,
|
limit: 0,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
for i, limit := range c.limits {
|
for i, limit := range c.limits {
|
||||||
indexer.limit = limit
|
indexer.limit = limit
|
||||||
|
|
@ -237,13 +236,13 @@ func TestTxIndexerRepair(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))
|
||||||
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts)
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: c.limit,
|
limit: c.limit,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
||||||
|
|
||||||
|
|
@ -428,19 +427,16 @@ func TestTxIndexerReport(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
encReceipts := types.EncodeBlockReceiptLists(append([]types.Receipts{{}}, receipts...))
|
||||||
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), encReceipts)
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: c.limit,
|
limit: c.limit,
|
||||||
cutoff: c.cutoff,
|
cutoff: c.cutoff,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
if c.tail != nil {
|
p := indexer.report(c.head, c.tail)
|
||||||
rawdb.WriteTxIndexTail(db, *c.tail)
|
|
||||||
}
|
|
||||||
p := indexer.report(c.head)
|
|
||||||
if p.Indexed != c.expIndexed {
|
if p.Indexed != c.expIndexed {
|
||||||
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1494,22 +1494,22 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T
|
||||||
// equal number for all for accounts with many pending transactions.
|
// equal number for all for accounts with many pending transactions.
|
||||||
func (pool *LegacyPool) truncatePending() {
|
func (pool *LegacyPool) truncatePending() {
|
||||||
pending := uint64(0)
|
pending := uint64(0)
|
||||||
for _, list := range pool.pending {
|
|
||||||
pending += uint64(list.Len())
|
// Assemble a spam order to penalize large transactors first
|
||||||
|
spammers := prque.New[uint64, common.Address](nil)
|
||||||
|
for addr, list := range pool.pending {
|
||||||
|
// Only evict transactions from high rollers
|
||||||
|
length := uint64(list.Len())
|
||||||
|
pending += length
|
||||||
|
if length > pool.config.AccountSlots {
|
||||||
|
spammers.Push(addr, length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if pending <= pool.config.GlobalSlots {
|
if pending <= pool.config.GlobalSlots {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingBeforeCap := pending
|
pendingBeforeCap := pending
|
||||||
// Assemble a spam order to penalize large transactors first
|
|
||||||
spammers := prque.New[int64, common.Address](nil)
|
|
||||||
for addr, list := range pool.pending {
|
|
||||||
// Only evict transactions from high rollers
|
|
||||||
if uint64(list.Len()) > pool.config.AccountSlots {
|
|
||||||
spammers.Push(addr, int64(list.Len()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Gradually drop transactions from offenders
|
// Gradually drop transactions from offenders
|
||||||
offenders := []common.Address{}
|
offenders := []common.Address{}
|
||||||
for pending > pool.config.GlobalSlots && !spammers.Empty() {
|
for pending > pool.config.GlobalSlots && !spammers.Empty() {
|
||||||
|
|
@ -1934,7 +1934,7 @@ func (pool *LegacyPool) Clear() {
|
||||||
pool.reserver.Release(addr)
|
pool.reserver.Release(addr)
|
||||||
}
|
}
|
||||||
pool.all.Clear()
|
pool.all.Clear()
|
||||||
pool.priced = newPricedList(pool.all)
|
pool.priced.Reheap()
|
||||||
pool.pending = make(map[common.Address]*list)
|
pool.pending = make(map[common.Address]*list)
|
||||||
pool.queue = make(map[common.Address]*list)
|
pool.queue = make(map[common.Address]*list)
|
||||||
pool.pendingNonces = newNoncer(pool.currentState)
|
pool.pendingNonces = newNoncer(pool.currentState)
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,13 @@ type Account struct {
|
||||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
||||||
Balance *big.Int `json:"balance" gencodec:"required"`
|
Balance *big.Int `json:"balance" gencodec:"required"`
|
||||||
Nonce uint64 `json:"nonce,omitempty"`
|
Nonce uint64 `json:"nonce,omitempty"`
|
||||||
|
|
||||||
// used in tests
|
|
||||||
PrivateKey []byte `json:"secretKey,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type accountMarshaling struct {
|
type accountMarshaling struct {
|
||||||
Code hexutil.Bytes
|
Code hexutil.Bytes
|
||||||
Balance *math.HexOrDecimal256
|
Balance *math.HexOrDecimal256
|
||||||
Nonce math.HexOrDecimal64
|
Nonce math.HexOrDecimal64
|
||||||
Storage map[storageJSON]storageJSON
|
Storage map[storageJSON]storageJSON
|
||||||
PrivateKey hexutil.Bytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,12 @@ func (b *Bloom) SetBytes(d []byte) {
|
||||||
|
|
||||||
// Add adds d to the filter. Future calls of Test(d) will return true.
|
// Add adds d to the filter. Future calls of Test(d) will return true.
|
||||||
func (b *Bloom) Add(d []byte) {
|
func (b *Bloom) Add(d []byte) {
|
||||||
b.add(d, make([]byte, 6))
|
var buf [6]byte
|
||||||
|
b.AddWithBuffer(d, &buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes)
|
// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes)
|
||||||
func (b *Bloom) add(d []byte, buf []byte) {
|
func (b *Bloom) AddWithBuffer(d []byte, buf *[6]byte) {
|
||||||
i1, v1, i2, v2, i3, v3 := bloomValues(d, buf)
|
i1, v1, i2, v2, i3, v3 := bloomValues(d, buf)
|
||||||
b[i1] |= v1
|
b[i1] |= v1
|
||||||
b[i2] |= v2
|
b[i2] |= v2
|
||||||
|
|
@ -84,7 +85,8 @@ func (b Bloom) Bytes() []byte {
|
||||||
|
|
||||||
// Test checks if the given topic is present in the bloom filter
|
// Test checks if the given topic is present in the bloom filter
|
||||||
func (b Bloom) Test(topic []byte) bool {
|
func (b Bloom) Test(topic []byte) bool {
|
||||||
i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6))
|
var buf [6]byte
|
||||||
|
i1, v1, i2, v2, i3, v3 := bloomValues(topic, &buf)
|
||||||
return v1 == v1&b[i1] &&
|
return v1 == v1&b[i1] &&
|
||||||
v2 == v2&b[i2] &&
|
v2 == v2&b[i2] &&
|
||||||
v3 == v3&b[i3]
|
v3 == v3&b[i3]
|
||||||
|
|
@ -104,12 +106,12 @@ func (b *Bloom) UnmarshalText(input []byte) error {
|
||||||
func CreateBloom(receipt *Receipt) Bloom {
|
func CreateBloom(receipt *Receipt) Bloom {
|
||||||
var (
|
var (
|
||||||
bin Bloom
|
bin Bloom
|
||||||
buf = make([]byte, 6)
|
buf [6]byte
|
||||||
)
|
)
|
||||||
for _, log := range receipt.Logs {
|
for _, log := range receipt.Logs {
|
||||||
bin.add(log.Address.Bytes(), buf)
|
bin.AddWithBuffer(log.Address.Bytes(), &buf)
|
||||||
for _, b := range log.Topics {
|
for _, b := range log.Topics {
|
||||||
bin.add(b[:], buf)
|
bin.AddWithBuffer(b[:], &buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bin
|
return bin
|
||||||
|
|
@ -139,21 +141,20 @@ func Bloom9(data []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bloomValues returns the bytes (index-value pairs) to set for the given data
|
// bloomValues returns the bytes (index-value pairs) to set for the given data
|
||||||
func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) {
|
func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) {
|
||||||
sha := hasherPool.Get().(crypto.KeccakState)
|
sha := hasherPool.Get().(crypto.KeccakState)
|
||||||
sha.Reset()
|
sha.Reset()
|
||||||
sha.Write(data)
|
sha.Write(data)
|
||||||
sha.Read(hashbuf)
|
sha.Read(hashbuf[:])
|
||||||
hasherPool.Put(sha)
|
hasherPool.Put(sha)
|
||||||
// The actual bits to flip
|
// The actual bits to flip
|
||||||
v1 := byte(1 << (hashbuf[1] & 0x7))
|
v1 := byte(1 << (hashbuf[1] & 0x7))
|
||||||
v2 := byte(1 << (hashbuf[3] & 0x7))
|
v2 := byte(1 << (hashbuf[3] & 0x7))
|
||||||
v3 := byte(1 << (hashbuf[5] & 0x7))
|
v3 := byte(1 << (hashbuf[5] & 0x7))
|
||||||
// The indices for the bytes to OR in
|
// The indices for the bytes to OR in
|
||||||
i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1
|
i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[0:])&0x7ff)>>3) - 1
|
||||||
i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1
|
i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1
|
||||||
i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1
|
i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1
|
||||||
|
|
||||||
return i1, v1, i2, v2, i3, v3
|
return i1, v1, i2, v2, i3, v3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@ var _ = (*accountMarshaling)(nil)
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (a Account) MarshalJSON() ([]byte, error) {
|
func (a Account) MarshalJSON() ([]byte, error) {
|
||||||
type Account struct {
|
type Account struct {
|
||||||
Code hexutil.Bytes `json:"code,omitempty"`
|
Code hexutil.Bytes `json:"code,omitempty"`
|
||||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||||
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
|
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||||
PrivateKey hexutil.Bytes `json:"secretKey,omitempty"`
|
|
||||||
}
|
}
|
||||||
var enc Account
|
var enc Account
|
||||||
enc.Code = a.Code
|
enc.Code = a.Code
|
||||||
|
|
@ -33,18 +32,16 @@ func (a Account) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
enc.Balance = (*math.HexOrDecimal256)(a.Balance)
|
enc.Balance = (*math.HexOrDecimal256)(a.Balance)
|
||||||
enc.Nonce = math.HexOrDecimal64(a.Nonce)
|
enc.Nonce = math.HexOrDecimal64(a.Nonce)
|
||||||
enc.PrivateKey = a.PrivateKey
|
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (a *Account) UnmarshalJSON(input []byte) error {
|
func (a *Account) UnmarshalJSON(input []byte) error {
|
||||||
type Account struct {
|
type Account struct {
|
||||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||||
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
|
||||||
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
|
||||||
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||||
PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"`
|
|
||||||
}
|
}
|
||||||
var dec Account
|
var dec Account
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -66,8 +63,5 @@ func (a *Account) UnmarshalJSON(input []byte) error {
|
||||||
if dec.Nonce != nil {
|
if dec.Nonce != nil {
|
||||||
a.Nonce = uint64(*dec.Nonce)
|
a.Nonce = uint64(*dec.Nonce)
|
||||||
}
|
}
|
||||||
if dec.PrivateKey != nil {
|
|
||||||
a.PrivateKey = *dec.PrivateKey
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
|
||||||
var hasherPool = sync.Pool{
|
var hasherPool = sync.Pool{
|
||||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
New: func() interface{} { return crypto.NewKeccakState() },
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,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/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -258,7 +259,7 @@ func (r *Receipt) Size() common.StorageSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
||||||
// that omits the Bloom field and deserialization that re-computes it.
|
// that omits the Bloom field. The Bloom field is recomputed by DeriveFields.
|
||||||
type ReceiptForStorage Receipt
|
type ReceiptForStorage Receipt
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||||
|
|
@ -291,7 +292,6 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||||
r.Logs = stored.Logs
|
r.Logs = stored.Logs
|
||||||
r.Bloom = CreateBloom((*Receipt)(r))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -372,6 +372,26 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
|
||||||
rs[i].Logs[j].Index = logIndex
|
rs[i].Logs[j].Index = logIndex
|
||||||
logIndex++
|
logIndex++
|
||||||
}
|
}
|
||||||
|
// also derive the Bloom if not derived yet
|
||||||
|
rs[i].Bloom = CreateBloom(rs[i])
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeBlockReceiptLists encodes a list of block receipt lists into RLP.
|
||||||
|
func EncodeBlockReceiptLists(receipts []Receipts) []rlp.RawValue {
|
||||||
|
var storageReceipts []*ReceiptForStorage
|
||||||
|
result := make([]rlp.RawValue, len(receipts))
|
||||||
|
for i, receipt := range receipts {
|
||||||
|
storageReceipts = storageReceipts[:0]
|
||||||
|
for _, r := range receipt {
|
||||||
|
storageReceipts = append(storageReceipts, (*ReceiptForStorage)(r))
|
||||||
|
}
|
||||||
|
bytes, err := rlp.EncodeToBytes(storageReceipts)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to encode block receipts", "err", err)
|
||||||
|
}
|
||||||
|
result[i] = bytes
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -154,148 +155,161 @@ var (
|
||||||
blockNumber = big.NewInt(1)
|
blockNumber = big.NewInt(1)
|
||||||
blockTime = uint64(2)
|
blockTime = uint64(2)
|
||||||
blockHash = common.BytesToHash([]byte{0x03, 0x14})
|
blockHash = common.BytesToHash([]byte{0x03, 0x14})
|
||||||
|
|
||||||
// Create the corresponding receipts
|
|
||||||
receipts = Receipts{
|
|
||||||
&Receipt{
|
|
||||||
Status: ReceiptStatusFailed,
|
|
||||||
CumulativeGasUsed: 1,
|
|
||||||
Logs: []*Log{
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x11}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[0].Hash(),
|
|
||||||
TxIndex: 0,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[0].Hash(),
|
|
||||||
TxIndex: 0,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[0].Hash(),
|
|
||||||
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
|
||||||
GasUsed: 1,
|
|
||||||
EffectiveGasPrice: big.NewInt(11),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 0,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
PostState: common.Hash{2}.Bytes(),
|
|
||||||
CumulativeGasUsed: 3,
|
|
||||||
Logs: []*Log{
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x22}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[1].Hash(),
|
|
||||||
TxIndex: 1,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
|
||||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
|
||||||
// derived fields:
|
|
||||||
BlockNumber: blockNumber.Uint64(),
|
|
||||||
TxHash: txs[1].Hash(),
|
|
||||||
TxIndex: 1,
|
|
||||||
BlockHash: blockHash,
|
|
||||||
Index: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[1].Hash(),
|
|
||||||
GasUsed: 2,
|
|
||||||
EffectiveGasPrice: big.NewInt(22),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 1,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: AccessListTxType,
|
|
||||||
PostState: common.Hash{3}.Bytes(),
|
|
||||||
CumulativeGasUsed: 6,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[2].Hash(),
|
|
||||||
GasUsed: 3,
|
|
||||||
EffectiveGasPrice: big.NewInt(33),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 2,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: DynamicFeeTxType,
|
|
||||||
PostState: common.Hash{4}.Bytes(),
|
|
||||||
CumulativeGasUsed: 10,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[3].Hash(),
|
|
||||||
GasUsed: 4,
|
|
||||||
EffectiveGasPrice: big.NewInt(1044),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 3,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: DynamicFeeTxType,
|
|
||||||
PostState: common.Hash{5}.Bytes(),
|
|
||||||
CumulativeGasUsed: 15,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[4].Hash(),
|
|
||||||
GasUsed: 5,
|
|
||||||
EffectiveGasPrice: big.NewInt(1055),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 4,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: BlobTxType,
|
|
||||||
PostState: common.Hash{6}.Bytes(),
|
|
||||||
CumulativeGasUsed: 21,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[5].Hash(),
|
|
||||||
GasUsed: 6,
|
|
||||||
EffectiveGasPrice: big.NewInt(1066),
|
|
||||||
BlobGasUsed: params.BlobTxBlobGasPerBlob,
|
|
||||||
BlobGasPrice: big.NewInt(920),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 5,
|
|
||||||
},
|
|
||||||
&Receipt{
|
|
||||||
Type: BlobTxType,
|
|
||||||
PostState: common.Hash{7}.Bytes(),
|
|
||||||
CumulativeGasUsed: 28,
|
|
||||||
Logs: []*Log{},
|
|
||||||
// derived fields:
|
|
||||||
TxHash: txs[6].Hash(),
|
|
||||||
GasUsed: 7,
|
|
||||||
EffectiveGasPrice: big.NewInt(1077),
|
|
||||||
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
|
|
||||||
BlobGasPrice: big.NewInt(920),
|
|
||||||
BlockHash: blockHash,
|
|
||||||
BlockNumber: blockNumber,
|
|
||||||
TransactionIndex: 6,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var receiptsOnce sync.Once
|
||||||
|
var testReceipts Receipts
|
||||||
|
|
||||||
|
func getTestReceipts() Receipts {
|
||||||
|
// Compute the blooms only once
|
||||||
|
receiptsOnce.Do(func() {
|
||||||
|
// Create the corresponding receipts
|
||||||
|
r := Receipts{
|
||||||
|
&Receipt{
|
||||||
|
Status: ReceiptStatusFailed,
|
||||||
|
CumulativeGasUsed: 1,
|
||||||
|
Logs: []*Log{
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x11}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
TxIndex: 0,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
TxIndex: 0,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||||
|
GasUsed: 1,
|
||||||
|
EffectiveGasPrice: big.NewInt(11),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 0,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
PostState: common.Hash{2}.Bytes(),
|
||||||
|
CumulativeGasUsed: 3,
|
||||||
|
Logs: []*Log{
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x22}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
TxIndex: 1,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
||||||
|
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||||
|
// derived fields:
|
||||||
|
BlockNumber: blockNumber.Uint64(),
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
TxIndex: 1,
|
||||||
|
BlockHash: blockHash,
|
||||||
|
Index: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
GasUsed: 2,
|
||||||
|
EffectiveGasPrice: big.NewInt(22),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 1,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: AccessListTxType,
|
||||||
|
PostState: common.Hash{3}.Bytes(),
|
||||||
|
CumulativeGasUsed: 6,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[2].Hash(),
|
||||||
|
GasUsed: 3,
|
||||||
|
EffectiveGasPrice: big.NewInt(33),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 2,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: DynamicFeeTxType,
|
||||||
|
PostState: common.Hash{4}.Bytes(),
|
||||||
|
CumulativeGasUsed: 10,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[3].Hash(),
|
||||||
|
GasUsed: 4,
|
||||||
|
EffectiveGasPrice: big.NewInt(1044),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 3,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: DynamicFeeTxType,
|
||||||
|
PostState: common.Hash{5}.Bytes(),
|
||||||
|
CumulativeGasUsed: 15,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[4].Hash(),
|
||||||
|
GasUsed: 5,
|
||||||
|
EffectiveGasPrice: big.NewInt(1055),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 4,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: BlobTxType,
|
||||||
|
PostState: common.Hash{6}.Bytes(),
|
||||||
|
CumulativeGasUsed: 21,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[5].Hash(),
|
||||||
|
GasUsed: 6,
|
||||||
|
EffectiveGasPrice: big.NewInt(1066),
|
||||||
|
BlobGasUsed: params.BlobTxBlobGasPerBlob,
|
||||||
|
BlobGasPrice: big.NewInt(920),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 5,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: BlobTxType,
|
||||||
|
PostState: common.Hash{7}.Bytes(),
|
||||||
|
CumulativeGasUsed: 28,
|
||||||
|
Logs: []*Log{},
|
||||||
|
// derived fields:
|
||||||
|
TxHash: txs[6].Hash(),
|
||||||
|
GasUsed: 7,
|
||||||
|
EffectiveGasPrice: big.NewInt(1077),
|
||||||
|
BlobGasUsed: 3 * params.BlobTxBlobGasPerBlob,
|
||||||
|
BlobGasPrice: big.NewInt(920),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 6,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, receipt := range r {
|
||||||
|
receipt.Bloom = CreateBloom(receipt)
|
||||||
|
}
|
||||||
|
testReceipts = r
|
||||||
|
})
|
||||||
|
return testReceipts
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||||
input := []byte{0x80}
|
input := []byte{0x80}
|
||||||
var r Receipt
|
var r Receipt
|
||||||
|
|
@ -310,6 +324,7 @@ func TestDeriveFields(t *testing.T) {
|
||||||
// Re-derive receipts.
|
// Re-derive receipts.
|
||||||
basefee := big.NewInt(1000)
|
basefee := big.NewInt(1000)
|
||||||
blobGasPrice := big.NewInt(920)
|
blobGasPrice := big.NewInt(920)
|
||||||
|
receipts := getTestReceipts()
|
||||||
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
||||||
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
|
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), blockTime, basefee, blobGasPrice, txs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -335,6 +350,7 @@ func TestDeriveFields(t *testing.T) {
|
||||||
// Test that we can marshal/unmarshal receipts to/from json without errors.
|
// Test that we can marshal/unmarshal receipts to/from json without errors.
|
||||||
// This also confirms that our test receipts contain all the required fields.
|
// This also confirms that our test receipts contain all the required fields.
|
||||||
func TestReceiptJSON(t *testing.T) {
|
func TestReceiptJSON(t *testing.T) {
|
||||||
|
receipts := getTestReceipts()
|
||||||
for i := range receipts {
|
for i := range receipts {
|
||||||
b, err := receipts[i].MarshalJSON()
|
b, err := receipts[i].MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -351,6 +367,7 @@ func TestReceiptJSON(t *testing.T) {
|
||||||
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
|
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
|
||||||
// though it is required per the spec.
|
// though it is required per the spec.
|
||||||
func TestEffectiveGasPriceNotRequired(t *testing.T) {
|
func TestEffectiveGasPriceNotRequired(t *testing.T) {
|
||||||
|
receipts := getTestReceipts()
|
||||||
r := *receipts[0]
|
r := *receipts[0]
|
||||||
r.EffectiveGasPrice = nil
|
r.EffectiveGasPrice = nil
|
||||||
b, err := r.MarshalJSON()
|
b, err := r.MarshalJSON()
|
||||||
|
|
@ -511,6 +528,7 @@ func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
|
||||||
cpy.EffectiveGasPrice = big.NewInt(0)
|
cpy.EffectiveGasPrice = big.NewInt(0)
|
||||||
cpy.BlobGasUsed = 0
|
cpy.BlobGasUsed = 0
|
||||||
cpy.BlobGasPrice = nil
|
cpy.BlobGasPrice = nil
|
||||||
|
cpy.Bloom = CreateBloom(&cpy)
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -355,28 +355,31 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
|
||||||
// Note: if the effective gasTipCap is negative, this method returns both error
|
// Note: if the effective gasTipCap is negative, this method returns both error
|
||||||
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
||||||
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
||||||
|
dst := new(big.Int)
|
||||||
|
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
|
return dst, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcEffectiveGasTip calculates the effective gas tip of the transaction and
|
||||||
|
// saves the result to dst.
|
||||||
|
func (tx *Transaction) calcEffectiveGasTip(dst *big.Int, baseFee *big.Int) error {
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCap(), nil
|
dst.Set(tx.inner.gasTipCap())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
gasFeeCap := tx.GasFeeCap()
|
gasFeeCap := tx.inner.gasFeeCap()
|
||||||
if gasFeeCap.Cmp(baseFee) < 0 {
|
if gasFeeCap.Cmp(baseFee) < 0 {
|
||||||
err = ErrGasFeeCapTooLow
|
err = ErrGasFeeCapTooLow
|
||||||
}
|
}
|
||||||
gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee)
|
|
||||||
|
|
||||||
gasTipCap := tx.GasTipCap()
|
dst.Sub(gasFeeCap, baseFee)
|
||||||
if gasTipCap.Cmp(gasFeeCap) < 0 {
|
gasTipCap := tx.inner.gasTipCap()
|
||||||
return gasTipCap, err
|
if gasTipCap.Cmp(dst) < 0 {
|
||||||
|
dst.Set(gasTipCap)
|
||||||
}
|
}
|
||||||
return gasFeeCap, err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an
|
|
||||||
// error in case the effective gasTipCap is negative
|
|
||||||
func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int {
|
|
||||||
effectiveTip, _ := tx.EffectiveGasTip(baseFee)
|
|
||||||
return effectiveTip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
||||||
|
|
@ -384,7 +387,11 @@ func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int)
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCapCmp(other)
|
return tx.GasTipCapCmp(other)
|
||||||
}
|
}
|
||||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee))
|
// Use more efficient internal method.
|
||||||
|
txTip, otherTip := new(big.Int), new(big.Int)
|
||||||
|
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||||
|
other.calcEffectiveGasTip(otherTip, baseFee)
|
||||||
|
return txTip.Cmp(otherTip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
||||||
|
|
@ -392,7 +399,9 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCapIntCmp(other)
|
return tx.GasTipCapIntCmp(other)
|
||||||
}
|
}
|
||||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
txTip := new(big.Int)
|
||||||
|
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||||
|
return txTip.Cmp(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
|
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -593,3 +594,101 @@ func BenchmarkHash(b *testing.B) {
|
||||||
signer.Hash(tx)
|
signer.Hash(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkEffectiveGasTip(b *testing.B) {
|
||||||
|
signer := LatestSigner(params.TestChainConfig)
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
txdata := &DynamicFeeTx{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
Nonce: 0,
|
||||||
|
GasTipCap: big.NewInt(2000000000),
|
||||||
|
GasFeeCap: big.NewInt(3000000000),
|
||||||
|
Gas: 21000,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Data: nil,
|
||||||
|
}
|
||||||
|
tx, _ := SignNewTx(key, signer, txdata)
|
||||||
|
baseFee := big.NewInt(1000000000) // 1 gwei
|
||||||
|
|
||||||
|
b.Run("Original", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := tx.EffectiveGasTip(baseFee)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IntoMethod", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
dst := new(big.Int)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEffectiveGasTipInto(t *testing.T) {
|
||||||
|
signer := LatestSigner(params.TestChainConfig)
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
tipCap int64
|
||||||
|
feeCap int64
|
||||||
|
baseFee *int64
|
||||||
|
}{
|
||||||
|
{tipCap: 1, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 10, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 50, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 100, feeCap: 100, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 1, feeCap: 50, baseFee: intPtr(50)},
|
||||||
|
{tipCap: 1, feeCap: 20, baseFee: intPtr(50)}, // Base fee higher than fee cap
|
||||||
|
{tipCap: 50, feeCap: 100, baseFee: intPtr(0)},
|
||||||
|
{tipCap: 50, feeCap: 100, baseFee: nil}, // nil base fee
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
txdata := &DynamicFeeTx{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
Nonce: 0,
|
||||||
|
GasTipCap: big.NewInt(tc.tipCap),
|
||||||
|
GasFeeCap: big.NewInt(tc.feeCap),
|
||||||
|
Gas: 21000,
|
||||||
|
To: &common.Address{},
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Data: nil,
|
||||||
|
}
|
||||||
|
tx, _ := SignNewTx(key, signer, txdata)
|
||||||
|
|
||||||
|
var baseFee *big.Int
|
||||||
|
if tc.baseFee != nil {
|
||||||
|
baseFee = big.NewInt(*tc.baseFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get result from original method
|
||||||
|
orig, origErr := tx.EffectiveGasTip(baseFee)
|
||||||
|
|
||||||
|
// Get result from new method
|
||||||
|
dst := new(big.Int)
|
||||||
|
newErr := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
|
|
||||||
|
// Compare results
|
||||||
|
if (origErr != nil) != (newErr != nil) {
|
||||||
|
t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if orig.Cmp(dst) != 0 {
|
||||||
|
t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create integer pointer
|
||||||
|
func intPtr(i int64) *int64 {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ var PrecompiledContractsPrague = PrecompiledContracts{
|
||||||
|
|
||||||
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
||||||
|
|
||||||
var PrecompiledContractsVerkle = PrecompiledContractsPrague
|
var PrecompiledContractsVerkle = PrecompiledContractsBerlin
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PrecompiledAddressesPrague []common.Address
|
PrecompiledAddressesPrague []common.Address
|
||||||
|
|
|
||||||
|
|
@ -339,12 +339,10 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
|
||||||
addr := common.Address(a.Bytes20())
|
addr := common.Address(a.Bytes20())
|
||||||
code := interpreter.evm.StateDB.GetCode(addr)
|
code := interpreter.evm.StateDB.GetCode(addr)
|
||||||
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||||
if !scope.Contract.IsSystemCall {
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false)
|
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
if consumed < wanted {
|
||||||
scope.Contract.Gas = 0
|
return nil, ErrOutOfGas
|
||||||
return nil, ErrOutOfGas
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
||||||
|
|
||||||
|
|
@ -367,9 +365,9 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||||
// advanced past this boundary.
|
// advanced past this boundary.
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
scope.Contract.Gas = 0
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -395,9 +393,9 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
|
|
||||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
|
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
|
||||||
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
|
||||||
scope.Contract.Gas = 0
|
if consumed < wanted {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,14 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
|
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||||
// add proof of absence to witness
|
// Add proof of absence to witness
|
||||||
wgas := evm.AccessEvents.AddAccount(addr, false)
|
// At this point, the read costs have already been charged, either because this
|
||||||
|
// is a direct tx call, in which case it's covered by the intrinsic gas, or because
|
||||||
|
// of a CALL instruction, in which case BASIC_DATA has been added to the access
|
||||||
|
// list in write mode. If there is enough gas paying for the addition of the code
|
||||||
|
// hash leaf to the access list, then account creation will proceed unimpaired.
|
||||||
|
// Thus, only pay for the creation of the code hash leaf here.
|
||||||
|
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas, false)
|
||||||
if gas < wgas {
|
if gas < wgas {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
return nil, 0, ErrOutOfGas
|
return nil, 0, ErrOutOfGas
|
||||||
|
|
@ -433,7 +439,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
|
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address)
|
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas)
|
||||||
if statelessGas > gas {
|
if statelessGas > gas {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -481,14 +487,14 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
}
|
}
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreateInitGas(address)
|
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas)
|
||||||
if statelessGas > gas {
|
if consumed < wanted {
|
||||||
return nil, common.Address{}, 0, ErrOutOfGas
|
return nil, common.Address{}, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractInit)
|
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
|
||||||
}
|
}
|
||||||
gas = gas - statelessGas
|
gas = gas - consumed
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
evm.Context.Transfer(evm.StateDB, caller, address, value)
|
||||||
|
|
||||||
|
|
@ -535,7 +541,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
|
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
|
||||||
|
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
|
if len(ret) > 0 && (consumed < wanted) {
|
||||||
return ret, ErrCodeStoreOutOfGas
|
return ret, ErrCodeStoreOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +563,8 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code))
|
inithash := crypto.HashData(evm.interpreter.hasher, code)
|
||||||
|
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -394,14 +394,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -428,16 +421,6 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
if evm.chainRules.IsEIP4762 && !contract.IsSystemCall {
|
|
||||||
address := common.Address(stack.Back(1).Bytes20())
|
|
||||||
transfersValue := !stack.Back(2).IsZero()
|
|
||||||
if transfersValue {
|
|
||||||
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
|
||||||
if overflow {
|
|
||||||
return 0, ErrGasUintOverflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
offset, size := scope.Stack.pop(), scope.Stack.peek()
|
||||||
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
if interpreter.hasher == nil {
|
interpreter.hasher.Reset()
|
||||||
interpreter.hasher = crypto.NewKeccakState()
|
|
||||||
} else {
|
|
||||||
interpreter.hasher.Reset()
|
|
||||||
}
|
|
||||||
interpreter.hasher.Write(data)
|
interpreter.hasher.Write(data)
|
||||||
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ type StateDB interface {
|
||||||
SelfDestruct6780(common.Address) (uint256.Int, bool)
|
SelfDestruct6780(common.Address) (uint256.Int, bool)
|
||||||
|
|
||||||
// Exist reports whether the given account exists in state.
|
// Exist reports whether the given account exists in state.
|
||||||
// Notably this should also return true for self-destructed accounts.
|
// Notably this also returns true for self-destructed accounts within the current transaction.
|
||||||
Exist(common.Address) bool
|
Exist(common.Address) bool
|
||||||
// Empty returns whether the given account is empty. Empty
|
// Empty returns whether the given account is empty. Empty
|
||||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
evm.Config.ExtraEips = extraEips
|
evm.Config.ExtraEips = extraEips
|
||||||
return &EVMInterpreter{evm: evm, table: table}
|
return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run loops and evaluates the contract's code with the given input data and returns
|
// Run loops and evaluates the contract's code with the given input data and returns
|
||||||
|
|
@ -237,7 +237,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
||||||
// associated costs.
|
// associated costs.
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false)
|
consumed, wanted := in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
|
||||||
|
contract.UseGas(consumed, in.evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
|
||||||
|
if consumed < wanted {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operation from the jump table and validate the stack to ensure there are
|
// Get the operation from the jump table and validate the stack to ensure there are
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ func validate(jt JumpTable) JumpTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newVerkleInstructionSet() JumpTable {
|
func newVerkleInstructionSet() JumpTable {
|
||||||
instructionSet := newCancunInstructionSet()
|
instructionSet := newShanghaiInstructionSet()
|
||||||
enable4762(&instructionSet)
|
enable4762(&instructionSet)
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,31 +25,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true)
|
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false)
|
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if contract.IsSystemCall {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
|
@ -57,56 +42,69 @@ func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
gas := evm.AccessEvents.BasicDataGas(address, false)
|
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
if contract.IsSystemCall {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
address := stack.peek().Bytes20()
|
address := stack.peek().Bytes20()
|
||||||
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
gas := evm.AccessEvents.CodeHashGas(address, false)
|
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil
|
||||||
if gas == 0 {
|
|
||||||
gas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
return gas, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
|
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
var (
|
||||||
if err != nil {
|
target = common.Address(stack.Back(1).Bytes20())
|
||||||
return 0, err
|
witnessGas uint64
|
||||||
}
|
_, isPrecompile = evm.precompile(target)
|
||||||
if contract.IsSystemCall {
|
isSystemContract = target == params.HistoryStorageAddress
|
||||||
return gas, nil
|
)
|
||||||
}
|
|
||||||
if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
|
// If value is transferred, it is charged before 1/64th
|
||||||
return gas, nil
|
// is subtracted from the available gas pool.
|
||||||
}
|
if withTransferCosts && !stack.Back(2).IsZero() {
|
||||||
witnessGas := evm.AccessEvents.MessageCallGas(contract.Address())
|
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas)
|
||||||
if witnessGas == 0 {
|
if wantedValueTransferWitnessGas > contract.Gas {
|
||||||
|
return wantedValueTransferWitnessGas, nil
|
||||||
|
}
|
||||||
|
witnessGas = wantedValueTransferWitnessGas
|
||||||
|
} else if isPrecompile || isSystemContract {
|
||||||
witnessGas = params.WarmStorageReadCostEIP2929
|
witnessGas = params.WarmStorageReadCostEIP2929
|
||||||
|
} else {
|
||||||
|
// The charging for the value transfer is done BEFORE subtracting
|
||||||
|
// the 1/64th gas, as this is considered part of the CALL instruction.
|
||||||
|
// (so before we get to this point)
|
||||||
|
// But the message call is part of the subcall, for which only 63/64th
|
||||||
|
// of the gas should be available.
|
||||||
|
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas)
|
||||||
|
var overflow bool
|
||||||
|
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if witnessGas > contract.Gas {
|
||||||
|
return witnessGas, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return witnessGas + gas, nil
|
|
||||||
|
contract.Gas -= witnessGas
|
||||||
|
// if the operation fails, adds witness gas to the gas before returning the error
|
||||||
|
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
|
contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall)
|
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall, true)
|
||||||
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode)
|
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode, false)
|
||||||
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall)
|
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall, false)
|
||||||
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall)
|
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
|
@ -118,15 +116,44 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
contractAddr := contract.Address()
|
contractAddr := contract.Address()
|
||||||
statelessGas := evm.AccessEvents.BasicDataGas(contractAddr, false)
|
wanted := evm.AccessEvents.BasicDataGas(contractAddr, false, contract.Gas, false)
|
||||||
|
if wanted > contract.Gas {
|
||||||
|
return wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas := wanted
|
||||||
|
balanceIsZero := evm.StateDB.GetBalance(contractAddr).Sign() == 0
|
||||||
|
_, isPrecompile := evm.precompile(beneficiaryAddr)
|
||||||
|
isSystemContract := beneficiaryAddr == params.HistoryStorageAddress
|
||||||
|
|
||||||
|
if (isPrecompile || isSystemContract) && balanceIsZero {
|
||||||
|
return statelessGas, nil
|
||||||
|
}
|
||||||
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, false)
|
wanted := evm.AccessEvents.BasicDataGas(beneficiaryAddr, false, contract.Gas-statelessGas, false)
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
}
|
}
|
||||||
// Charge write costs if it transfers value
|
// Charge write costs if it transfers value
|
||||||
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
if !balanceIsZero {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(contractAddr, true)
|
wanted := evm.AccessEvents.BasicDataGas(contractAddr, true, contract.Gas-statelessGas, false)
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
|
|
||||||
if contractAddr != beneficiaryAddr {
|
if contractAddr != beneficiaryAddr {
|
||||||
statelessGas += evm.AccessEvents.BasicDataGas(beneficiaryAddr, true)
|
if evm.StateDB.Exist(beneficiaryAddr) {
|
||||||
|
wanted = evm.AccessEvents.BasicDataGas(beneficiaryAddr, true, contract.Gas-statelessGas, false)
|
||||||
|
} else {
|
||||||
|
wanted = evm.AccessEvents.AddAccount(beneficiaryAddr, true, contract.Gas-statelessGas)
|
||||||
|
}
|
||||||
|
if wanted > contract.Gas-statelessGas {
|
||||||
|
return statelessGas + wanted, nil
|
||||||
|
}
|
||||||
|
statelessGas += wanted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statelessGas, nil
|
return statelessGas, nil
|
||||||
|
|
@ -137,17 +164,19 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
var (
|
|
||||||
codeOffset = stack.Back(1)
|
|
||||||
length = stack.Back(2)
|
|
||||||
)
|
|
||||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
|
||||||
if overflow {
|
|
||||||
uint64CodeOffset = gomath.MaxUint64
|
|
||||||
}
|
|
||||||
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
|
||||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||||
gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
|
var (
|
||||||
|
codeOffset = stack.Back(1)
|
||||||
|
length = stack.Back(2)
|
||||||
|
)
|
||||||
|
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
|
uint64CodeOffset = gomath.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||||
|
_, wanted := evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false, contract.Gas-gas)
|
||||||
|
gas += wanted
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
@ -158,16 +187,17 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if contract.IsSystemCall {
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
|
_, isPrecompile := evm.precompile(addr)
|
||||||
|
if isPrecompile || addr == params.HistoryStorageAddress {
|
||||||
|
var overflow bool
|
||||||
|
if gas, overflow = math.SafeAdd(gas, params.WarmStorageReadCostEIP2929); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
wgas := evm.AccessEvents.BasicDataGas(addr, false, contract.Gas-gas, true)
|
||||||
wgas := evm.AccessEvents.BasicDataGas(addr, false)
|
|
||||||
if wgas == 0 {
|
|
||||||
wgas = params.WarmStorageReadCostEIP2929
|
|
||||||
}
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
|
||||||
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -73,6 +74,12 @@ func NewKeccakState() KeccakState {
|
||||||
return sha3.NewLegacyKeccak256().(KeccakState)
|
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasherPool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return sha3.NewLegacyKeccak256().(KeccakState)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
|
||||||
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
||||||
kh.Reset()
|
kh.Reset()
|
||||||
|
|
@ -84,22 +91,26 @@ func HashData(kh KeccakState, data []byte) (h common.Hash) {
|
||||||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||||
func Keccak256(data ...[]byte) []byte {
|
func Keccak256(data ...[]byte) []byte {
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
d := NewKeccakState()
|
d := hasherPool.Get().(KeccakState)
|
||||||
|
d.Reset()
|
||||||
for _, b := range data {
|
for _, b := range data {
|
||||||
d.Write(b)
|
d.Write(b)
|
||||||
}
|
}
|
||||||
d.Read(b)
|
d.Read(b)
|
||||||
|
hasherPool.Put(d)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
||||||
// converting it to an internal Hash data structure.
|
// converting it to an internal Hash data structure.
|
||||||
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||||
d := NewKeccakState()
|
d := hasherPool.Get().(KeccakState)
|
||||||
|
d.Reset()
|
||||||
for _, b := range data {
|
for _, b := range data {
|
||||||
d.Write(b)
|
d.Write(b)
|
||||||
}
|
}
|
||||||
d.Read(h[:])
|
d.Read(h[:])
|
||||||
|
hasherPool.Put(d)
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,17 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
return gokzgVerifyBlobProof(blob, commitment, proof)
|
return gokzgVerifyBlobProof(blob, commitment, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func ComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
if useCKZG.Load() {
|
||||||
|
return ckzgComputeCellProofs(blob)
|
||||||
|
}
|
||||||
|
return gokzgComputeCellProofs(blob)
|
||||||
|
}
|
||||||
|
|
||||||
// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
|
// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
|
||||||
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
|
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
|
||||||
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
|
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||||
ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
|
ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -47,15 +47,21 @@ func ckzgInit() {
|
||||||
if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
|
if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
g1s := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
|
g1Lag := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
|
||||||
for i, g1 := range params.SetupG1Lagrange {
|
for i, g1 := range params.SetupG1Lagrange {
|
||||||
|
copy(g1Lag[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
||||||
|
}
|
||||||
|
g1s := make([]byte, len(params.SetupG1Monomial)*(len(params.SetupG1Monomial[0])-2)/2)
|
||||||
|
for i, g1 := range params.SetupG1Monomial {
|
||||||
copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
||||||
}
|
}
|
||||||
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
|
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
|
||||||
for i, g2 := range params.SetupG2 {
|
for i, g2 := range params.SetupG2 {
|
||||||
copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
|
copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
|
||||||
}
|
}
|
||||||
if err = ckzg4844.LoadTrustedSetup(g1s, g2s); err != nil {
|
// The last parameter determines the multiplication table, see https://notes.ethereum.org/@jtraglia/windowed_multiplications
|
||||||
|
// I think 6 is an decent compromise between size and speed
|
||||||
|
if err = ckzg4844.LoadTrustedSetup(g1s, g1Lag, g2s, 6); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,3 +131,21 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
ckzgIniter.Do(ckzgInit)
|
||||||
|
|
||||||
|
_, proofs, err := ckzg4844.ComputeCellsAndKZGProofs((*ckzg4844.Blob)(blob))
|
||||||
|
if err != nil {
|
||||||
|
return []Proof{}, err
|
||||||
|
}
|
||||||
|
var p []Proof
|
||||||
|
for _, proof := range proofs {
|
||||||
|
p = append(p, (Proof)(proof))
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,3 +60,11 @@ func ckzgComputeBlobProof(blob *Blob, commitment Commitment) (Proof, error) {
|
||||||
func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
panic("unsupported platform")
|
panic("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
panic("unsupported platform")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// context is the crypto primitive pre-seeded with the trusted setup parameters.
|
// context is the crypto primitive pre-seeded with the trusted setup parameters.
|
||||||
|
|
@ -96,3 +96,21 @@ func gokzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error
|
||||||
|
|
||||||
return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
|
return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gokzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
gokzgIniter.Do(gokzgInit)
|
||||||
|
|
||||||
|
_, proofs, err := context.ComputeCellsAndKZGProofs((*gokzg4844.Blob)(blob), 0)
|
||||||
|
if err != nil {
|
||||||
|
return []Proof{}, err
|
||||||
|
}
|
||||||
|
var p []Proof
|
||||||
|
for _, proof := range proofs {
|
||||||
|
p = append(p, (Proof)(proof))
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func randFieldElement() [32]byte {
|
func randFieldElement() [32]byte {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -349,22 +349,20 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
||||||
// GetTransaction retrieves the lookup along with the transaction itself associate
|
// GetTransaction retrieves the lookup along with the transaction itself associate
|
||||||
// with the given transaction hash.
|
// with the given transaction hash.
|
||||||
//
|
//
|
||||||
// An error will be returned if the transaction is not found, and background
|
// A null will be returned if the transaction is not found. The transaction is not
|
||||||
// indexing for transactions is still in progress. The error is used to indicate the
|
// existent from the node's perspective. This can be due to the transaction indexer
|
||||||
// scenario explicitly that the transaction might be reachable shortly.
|
// not being finished. The caller must explicitly check the indexer progress.
|
||||||
//
|
func (b *EthAPIBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
// A null will be returned in the transaction is not found and background transaction
|
lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||||
// indexing is already finished. The transaction is not existent from the perspective
|
|
||||||
// of node.
|
|
||||||
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
|
||||||
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
|
|
||||||
if err != nil {
|
|
||||||
return false, nil, common.Hash{}, 0, 0, err
|
|
||||||
}
|
|
||||||
if lookup == nil || tx == nil {
|
if lookup == nil || tx == nil {
|
||||||
return false, nil, common.Hash{}, 0, 0, nil
|
return false, nil, common.Hash{}, 0, 0
|
||||||
}
|
}
|
||||||
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
|
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||||
|
func (b *EthAPIBackend) TxIndexDone() bool {
|
||||||
|
return b.eth.blockchain.TxIndexDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
|
@ -391,7 +389,7 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
|
||||||
return b.eth.txPool.SubscribeTransactions(ch, true)
|
return b.eth.txPool.SubscribeTransactions(ch, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
|
func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
prog := b.eth.Downloader().Progress()
|
prog := b.eth.Downloader().Progress()
|
||||||
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
||||||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||||
|
|
@ -443,6 +441,14 @@ func (b *EthAPIBackend) RPCTxFeeCap() float64 {
|
||||||
return b.eth.config.RPCTxFeeCap
|
return b.eth.config.RPCTxFeeCap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) CurrentView() *filtermaps.ChainView {
|
||||||
|
head := b.eth.blockchain.CurrentBlock()
|
||||||
|
if head == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return filtermaps.NewChainView(b.eth.blockchain, head.Number.Uint64(), head.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) NewMatcherBackend() filtermaps.MatcherBackend {
|
func (b *EthAPIBackend) NewMatcherBackend() filtermaps.MatcherBackend {
|
||||||
return b.eth.filterMaps.NewMatcherBackend()
|
return b.eth.filterMaps.NewMatcherBackend()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,13 +134,17 @@ func TestSendTx(t *testing.T) {
|
||||||
func testSendTx(t *testing.T, withLocal bool) {
|
func testSendTx(t *testing.T, withLocal bool) {
|
||||||
b := initBackend(withLocal)
|
b := initBackend(withLocal)
|
||||||
|
|
||||||
txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{
|
txA := pricedSetCodeTx(0, 250000, uint256.NewInt(params.GWei), uint256.NewInt(params.GWei), key, []unsignedAuth{{nonce: 0, key: key}})
|
||||||
{
|
if err := b.SendTx(context.Background(), txA); err != nil {
|
||||||
nonce: 0,
|
t.Fatalf("Failed to submit tx: %v", err)
|
||||||
key: key,
|
}
|
||||||
},
|
for {
|
||||||
})
|
pending, _ := b.TxPool().ContentFrom(address)
|
||||||
b.SendTx(context.Background(), txA)
|
if len(pending) == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
txB := makeTx(1, nil, nil, key)
|
txB := makeTx(1, nil, nil, key)
|
||||||
err := b.SendTx(context.Background(), txB)
|
err := b.SendTx(context.Background(), txB)
|
||||||
|
|
|
||||||
|
|
@ -271,26 +271,26 @@ func storageRangeAt(statedb *state.StateDB, root common.Hash, address common.Add
|
||||||
//
|
//
|
||||||
// With one parameter, returns the list of accounts modified in the specified block.
|
// With one parameter, returns the list of accounts modified in the specified block.
|
||||||
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
|
||||||
var startBlock, endBlock *types.Block
|
var startHeader, endHeader *types.Header
|
||||||
|
|
||||||
startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
|
startHeader = api.eth.blockchain.GetHeaderByNumber(startNum)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("start block %x not found", startNum)
|
return nil, fmt.Errorf("start block %x not found", startNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endNum == nil {
|
if endNum == nil {
|
||||||
endBlock = startBlock
|
endHeader = startHeader
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
endBlock = api.eth.blockchain.GetBlockByNumber(*endNum)
|
endHeader = api.eth.blockchain.GetHeaderByNumber(*endNum)
|
||||||
if endBlock == nil {
|
if endHeader == nil {
|
||||||
return nil, fmt.Errorf("end block %d not found", *endNum)
|
return nil, fmt.Errorf("end block %d not found", *endNum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.getModifiedAccounts(startBlock, endBlock)
|
return api.getModifiedAccounts(startHeader, endHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
// GetModifiedAccountsByHash returns all accounts that have changed between the
|
||||||
|
|
@ -299,38 +299,38 @@ func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64
|
||||||
//
|
//
|
||||||
// With one parameter, returns the list of accounts modified in the specified block.
|
// With one parameter, returns the list of accounts modified in the specified block.
|
||||||
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
|
||||||
var startBlock, endBlock *types.Block
|
var startHeader, endHeader *types.Header
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startHash)
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("start block %x not found", startHash)
|
return nil, fmt.Errorf("start block %x not found", startHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if endHash == nil {
|
if endHash == nil {
|
||||||
endBlock = startBlock
|
endHeader = startHeader
|
||||||
startBlock = api.eth.blockchain.GetBlockByHash(startBlock.ParentHash())
|
startHeader = api.eth.blockchain.GetHeaderByHash(startHeader.ParentHash)
|
||||||
if startBlock == nil {
|
if startHeader == nil {
|
||||||
return nil, fmt.Errorf("block %x has no parent", endBlock.Number())
|
return nil, fmt.Errorf("block %x has no parent", endHeader.Number)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
endBlock = api.eth.blockchain.GetBlockByHash(*endHash)
|
endHeader = api.eth.blockchain.GetHeaderByHash(*endHash)
|
||||||
if endBlock == nil {
|
if endHeader == nil {
|
||||||
return nil, fmt.Errorf("end block %x not found", *endHash)
|
return nil, fmt.Errorf("end block %x not found", *endHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.getModifiedAccounts(startBlock, endBlock)
|
return api.getModifiedAccounts(startHeader, endHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
|
func (api *DebugAPI) getModifiedAccounts(startHeader, endHeader *types.Header) ([]common.Address, error) {
|
||||||
if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
|
if startHeader.Number.Uint64() >= endHeader.Number.Uint64() {
|
||||||
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
|
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startHeader.Number.Uint64(), endHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
triedb := api.eth.BlockChain().TrieDB()
|
triedb := api.eth.BlockChain().TrieDB()
|
||||||
|
|
||||||
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startBlock.Root()), triedb)
|
oldTrie, err := trie.NewStateTrie(trie.StateTrieID(startHeader.Root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb)
|
newTrie, err := trie.NewStateTrie(trie.StateTrieID(endHeader.Root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,74 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dumper = spew.ConfigState{Indent: " "}
|
var dumper = spew.ConfigState{Indent: " "}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
addr common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAccounts(n int) (accounts []Account) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
accounts = append(accounts, Account{key: key, addr: addr})
|
||||||
|
}
|
||||||
|
slices.SortFunc(accounts, func(a, b Account) int { return a.addr.Cmp(b.addr) })
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestBlockChain creates a new test blockchain. OBS: After test is done, teardown must be
|
||||||
|
// invoked in order to release associated resources.
|
||||||
|
func newTestBlockChain(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *core.BlockChain {
|
||||||
|
engine := ethash.NewFaker()
|
||||||
|
// Generate blocks for testing
|
||||||
|
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
|
||||||
|
|
||||||
|
// Import the canonical chain
|
||||||
|
cacheConfig := &core.CacheConfig{
|
||||||
|
TrieCleanLimit: 256,
|
||||||
|
TrieDirtyLimit: 256,
|
||||||
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
|
SnapshotLimit: 0,
|
||||||
|
Preimages: true,
|
||||||
|
TrieDirtyDisabled: true, // Archive mode
|
||||||
|
}
|
||||||
|
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), cacheConfig, gspec, nil, engine, vm.Config{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
func accountRangeTest(t *testing.T, trie *state.Trie, statedb *state.StateDB, start common.Hash, requestedNum int, expectedNum int) state.Dump {
|
||||||
result := statedb.RawDump(&state.DumpConfig{
|
result := statedb.RawDump(&state.DumpConfig{
|
||||||
SkipCode: true,
|
SkipCode: true,
|
||||||
|
|
@ -224,3 +273,66 @@ func TestStorageRangeAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetModifiedAccounts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Initialize test accounts
|
||||||
|
accounts := newAccounts(4)
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genBlocks := 1
|
||||||
|
signer := types.HomesteadSigner{}
|
||||||
|
blockChain := newTestBlockChain(t, genBlocks, genesis, func(_ int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
for _, account := range accounts[:3] {
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 0,
|
||||||
|
To: &accounts[3].addr,
|
||||||
|
Value: big.NewInt(1000),
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasPrice: b.BaseFee(),
|
||||||
|
Data: nil}),
|
||||||
|
signer, account.key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer blockChain.Stop()
|
||||||
|
|
||||||
|
// Create a debug API instance.
|
||||||
|
api := NewDebugAPI(&Ethereum{blockchain: blockChain})
|
||||||
|
|
||||||
|
// Test GetModifiedAccountsByNumber
|
||||||
|
t.Run("GetModifiedAccountsByNumber", func(t *testing.T) {
|
||||||
|
addrs, err := api.GetModifiedAccountsByNumber(uint64(genBlocks), nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||||
|
for _, account := range accounts {
|
||||||
|
if !slices.Contains(addrs, account.addr) {
|
||||||
|
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test GetModifiedAccountsByHash
|
||||||
|
t.Run("GetModifiedAccountsByHash", func(t *testing.T) {
|
||||||
|
header := blockChain.GetHeaderByNumber(uint64(genBlocks))
|
||||||
|
addrs, err := api.GetModifiedAccountsByHash(header.Hash(), nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, addrs, len(accounts)+1) // +1 for the coinbase
|
||||||
|
for _, account := range accounts {
|
||||||
|
if !slices.Contains(addrs, account.addr) {
|
||||||
|
t.Fatalf("account %s not found in modified accounts", account.addr.Hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"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/forks"
|
"github.com/ethereum/go-ethereum/params/forks"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
@ -196,11 +197,11 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
|
||||||
// and return its payloadID.
|
// and return its payloadID.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if payloadAttributes != nil {
|
if payloadAttributes != nil {
|
||||||
if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1"))
|
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1")
|
||||||
if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) {
|
case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
|
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
|
||||||
|
|
@ -210,20 +211,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa
|
||||||
// attributes. It supports both PayloadAttributesV1 and PayloadAttributesV2.
|
// attributes. It supports both PayloadAttributesV1 and PayloadAttributesV2.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root"))
|
case params.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("unexpected beacon root")
|
||||||
switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) {
|
case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil:
|
||||||
case forks.Paris:
|
return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai")
|
||||||
if params.Withdrawals != nil {
|
case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
case forks.Shanghai:
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||||
if params.Withdrawals == nil {
|
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
|
return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
|
||||||
|
|
@ -233,14 +229,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa
|
||||||
// in the payload attributes. It supports only PayloadAttributesV3.
|
// in the payload attributes. It supports only PayloadAttributesV3.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
if params.BeaconRoot == nil {
|
case params.BeaconRoot == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root"))
|
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
||||||
|
|
@ -254,11 +249,11 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa
|
||||||
// generates an execution witness too if block building was requested.
|
// generates an execution witness too if block building was requested.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if payloadAttributes != nil {
|
if payloadAttributes != nil {
|
||||||
if payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("withdrawals and beacon root not supported in V1"))
|
case payloadAttributes.Withdrawals != nil || payloadAttributes.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, paramsErr("withdrawals and beacon root not supported in V1")
|
||||||
if api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, payloadAttributes.Timestamp) {
|
case !api.checkFork(payloadAttributes.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
|
return engine.STATUS_INVALID, paramsErr("fcuV1 called post-shanghai")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true)
|
return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, true)
|
||||||
|
|
@ -268,20 +263,15 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV1(update engine.Forkchoice
|
||||||
// generates an execution witness too if block building was requested.
|
// generates an execution witness too if block building was requested.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.BeaconRoot != nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("unexpected beacon root"))
|
case params.BeaconRoot != nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("unexpected beacon root")
|
||||||
switch api.eth.BlockChain().Config().LatestFork(params.Timestamp) {
|
case api.checkFork(params.Timestamp, forks.Paris) && params.Withdrawals != nil:
|
||||||
case forks.Paris:
|
return engine.STATUS_INVALID, attributesErr("withdrawals before shanghai")
|
||||||
if params.Withdrawals != nil {
|
case api.checkFork(params.Timestamp, forks.Shanghai) && params.Withdrawals == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Paris, forks.Shanghai):
|
||||||
case forks.Shanghai:
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV2 must only be called with paris or shanghai payloads")
|
||||||
if params.Withdrawals == nil {
|
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called with paris and shanghai payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, params, engine.PayloadV2, true)
|
return api.forkchoiceUpdated(update, params, engine.PayloadV2, true)
|
||||||
|
|
@ -291,14 +281,13 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV2(update engine.Forkchoice
|
||||||
// generates an execution witness too if block building was requested.
|
// generates an execution witness too if block building was requested.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing withdrawals"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
|
||||||
if params.BeaconRoot == nil {
|
case params.BeaconRoot == nil:
|
||||||
return engine.STATUS_INVALID, engine.InvalidPayloadAttributes.With(errors.New("missing beacon root"))
|
return engine.STATUS_INVALID, attributesErr("missing beacon root")
|
||||||
}
|
case !api.checkFork(params.Timestamp, forks.Cancun, forks.Prague):
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun && api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3 must only be called for cancun or prague payloads")
|
||||||
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
// TODO(matt): the spec requires that fcu is applied when called on a valid
|
||||||
|
|
@ -373,8 +362,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
}
|
}
|
||||||
valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
|
valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
|
||||||
return engine.ForkChoiceResponse{
|
return engine.ForkChoiceResponse{
|
||||||
PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash},
|
PayloadStatus: engine.PayloadStatusV1{
|
||||||
PayloadID: id,
|
Status: engine.VALID,
|
||||||
|
LatestValidHash: &update.HeadBlockHash,
|
||||||
|
},
|
||||||
|
PayloadID: id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
|
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
|
||||||
|
|
@ -465,7 +457,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
|
||||||
api.lastTransitionUpdate = time.Now()
|
api.lastTransitionUpdate = time.Now()
|
||||||
api.lastTransitionLock.Unlock()
|
api.lastTransitionLock.Unlock()
|
||||||
|
|
||||||
ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty
|
ttd := api.config().TerminalTotalDifficulty
|
||||||
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
|
||||||
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
|
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
|
||||||
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
|
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
|
||||||
|
|
@ -547,86 +539,74 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for NewPayload* methods.
|
||||||
|
var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID}
|
||||||
|
|
||||||
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals != nil {
|
if params.Withdrawals != nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
|
return invalidStatus, paramsErr("withdrawals not supported in V1")
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil, nil, nil, false)
|
return api.newPayload(params, nil, nil, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
var (
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||||
}
|
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
)
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case cancun:
|
||||||
}
|
return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun")
|
||||||
} else {
|
case shanghai && params.Withdrawals == nil:
|
||||||
if params.Withdrawals != nil {
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
case !shanghai && params.Withdrawals != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai")
|
||||||
}
|
case params.ExcessBlobGas != nil:
|
||||||
if params.ExcessBlobGas != nil {
|
return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
case params.BlobGasUsed != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||||
if params.BlobGasUsed != nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil, nil, nil, false)
|
return api.newPayload(params, nil, nil, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||||
}
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
if beaconRoot == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, versionedHashes, beaconRoot, nil, false)
|
return api.newPayload(params, versionedHashes, beaconRoot, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case executionRequests == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||||
if beaconRoot == nil {
|
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
|
||||||
if executionRequests == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads"))
|
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
if err := validateRequests(requests); err != nil {
|
if err := validateRequests(requests); err != nil {
|
||||||
|
|
@ -647,23 +627,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) (
|
||||||
// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates
|
// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates
|
||||||
// and returns a stateless witness after running the payload.
|
// and returns a stateless witness after running the payload.
|
||||||
func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
var (
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||||
}
|
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
)
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case cancun:
|
||||||
}
|
return invalidStatus, paramsErr("can't use newPayloadV2 post-cancun")
|
||||||
} else {
|
case shanghai && params.Withdrawals == nil:
|
||||||
if params.Withdrawals != nil {
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
case !shanghai && params.Withdrawals != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil withdrawals pre-shanghai")
|
||||||
}
|
case params.ExcessBlobGas != nil:
|
||||||
if params.ExcessBlobGas != nil {
|
return invalidStatus, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
case params.BlobGasUsed != nil:
|
||||||
}
|
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||||
if params.BlobGasUsed != nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil, nil, nil, true)
|
return api.newPayload(params, nil, nil, nil, true)
|
||||||
}
|
}
|
||||||
|
|
@ -671,25 +649,19 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (
|
||||||
// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates
|
// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates
|
||||||
// and returns a stateless witness after running the payload.
|
// and returns a stateless witness after running the payload.
|
||||||
func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||||
}
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
if beaconRoot == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, versionedHashes, beaconRoot, nil, true)
|
return api.newPayload(params, versionedHashes, beaconRoot, nil, true)
|
||||||
}
|
}
|
||||||
|
|
@ -697,28 +669,21 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v
|
||||||
// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates
|
// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates
|
||||||
// and returns a stateless witness after running the payload.
|
// and returns a stateless witness after running the payload.
|
||||||
func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return invalidStatus, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return invalidStatus, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return invalidStatus, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case executionRequests == nil:
|
||||||
}
|
return invalidStatus, paramsErr("nil executionRequests post-prague")
|
||||||
if beaconRoot == nil {
|
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
|
||||||
if executionRequests == nil {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads"))
|
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
if err := validateRequests(requests); err != nil {
|
if err := validateRequests(requests); err != nil {
|
||||||
|
|
@ -739,23 +704,21 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData,
|
||||||
// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in
|
// ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in
|
||||||
// a stateless mode on top of a provided witness instead of the local database.
|
// a stateless mode on top of a provided witness instead of the local database.
|
||||||
func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
|
var (
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use newPayloadV2 post-cancun"))
|
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
|
||||||
}
|
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
|
)
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case cancun:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("can't use newPayloadV2 post-cancun")
|
||||||
} else {
|
case shanghai && params.Withdrawals == nil:
|
||||||
if params.Withdrawals != nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
case !shanghai && params.Withdrawals != nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil withdrawals pre-shanghai")
|
||||||
}
|
case params.ExcessBlobGas != nil:
|
||||||
if params.ExcessBlobGas != nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil excessBlobGas pre-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
case params.BlobGasUsed != nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("non-nil blobGasUsed pre-cancun")
|
||||||
if params.BlobGasUsed != nil {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun"))
|
|
||||||
}
|
}
|
||||||
return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness)
|
return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness)
|
||||||
}
|
}
|
||||||
|
|
@ -763,25 +726,19 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData,
|
||||||
// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in
|
// ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in
|
||||||
// a stateless mode on top of a provided witness instead of the local database.
|
// a stateless mode on top of a provided witness instead of the local database.
|
||||||
func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case !api.checkFork(params.Timestamp, forks.Cancun):
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
if beaconRoot == nil {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV3 must only be called for cancun payloads"))
|
|
||||||
}
|
}
|
||||||
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness)
|
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness)
|
||||||
}
|
}
|
||||||
|
|
@ -789,30 +746,26 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData,
|
||||||
// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in
|
// ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in
|
||||||
// a stateless mode on top of a provided witness instead of the local database.
|
// a stateless mode on top of a provided witness instead of the local database.
|
||||||
func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) {
|
||||||
if params.Withdrawals == nil {
|
switch {
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
case params.Withdrawals == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil withdrawals post-shanghai")
|
||||||
if params.ExcessBlobGas == nil {
|
case params.ExcessBlobGas == nil:
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil excessBlobGas post-cancun")
|
||||||
}
|
case params.BlobGasUsed == nil:
|
||||||
if params.BlobGasUsed == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil blobGasUsed post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun"))
|
case versionedHashes == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil versionedHashes post-cancun")
|
||||||
|
case beaconRoot == nil:
|
||||||
if versionedHashes == nil {
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun")
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
case executionRequests == nil:
|
||||||
}
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague")
|
||||||
if beaconRoot == nil {
|
case !api.checkFork(params.Timestamp, forks.Prague):
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun"))
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
|
||||||
}
|
|
||||||
if executionRequests == nil {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague {
|
|
||||||
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads"))
|
|
||||||
}
|
}
|
||||||
requests := convertRequests(executionRequests)
|
requests := convertRequests(executionRequests)
|
||||||
|
if err := validateRequests(requests); err != nil {
|
||||||
|
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err)
|
||||||
|
}
|
||||||
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness)
|
return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -978,7 +931,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v
|
||||||
api.lastNewPayloadLock.Unlock()
|
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.eth.BlockChain().Config(), vm.Config{}, block, witness)
|
stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
|
log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
|
||||||
errorMsg := err.Error()
|
errorMsg := err.Error()
|
||||||
|
|
@ -1116,7 +1069,7 @@ func (api *ConsensusAPI) heartbeat() {
|
||||||
time.Sleep(beaconUpdateStartupTimeout)
|
time.Sleep(beaconUpdateStartupTimeout)
|
||||||
|
|
||||||
// If the network is not yet merged/merging, don't bother continuing.
|
// If the network is not yet merged/merging, don't bother continuing.
|
||||||
if api.eth.BlockChain().Config().TerminalTotalDifficulty == nil {
|
if api.config().TerminalTotalDifficulty == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1160,6 +1113,23 @@ func (api *ConsensusAPI) heartbeat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// config retrieves the chain's fork configuration.
|
||||||
|
func (api *ConsensusAPI) config() *params.ChainConfig {
|
||||||
|
return api.eth.BlockChain().Config()
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFork returns true if the latest fork at the given timestamp
|
||||||
|
// is one of the forks provided.
|
||||||
|
func (api *ConsensusAPI) checkFork(timestamp uint64, forks ...forks.Fork) bool {
|
||||||
|
latest := api.config().LatestFork(timestamp)
|
||||||
|
for _, fork := range forks {
|
||||||
|
if latest == fork {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ExchangeCapabilities returns the current methods provided by this node.
|
// ExchangeCapabilities returns the current methods provided by this node.
|
||||||
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
||||||
return caps
|
return caps
|
||||||
|
|
@ -1285,3 +1255,21 @@ func validateRequests(requests [][]byte) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paramsErr is a helper function for creating an InvalidPayloadAttributes
|
||||||
|
// Engine API error.
|
||||||
|
func paramsErr(msg string) error {
|
||||||
|
return engine.InvalidParams.With(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributesErr is a helper function for creating an InvalidPayloadAttributes
|
||||||
|
// Engine API error.
|
||||||
|
func attributesErr(msg string) error {
|
||||||
|
return engine.InvalidPayloadAttributes.With(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsupportedForkErr is a helper function for creating an UnsupportedFork
|
||||||
|
// Engine API error.
|
||||||
|
func unsupportedForkErr(msg string) error {
|
||||||
|
return engine.UnsupportedFork.With(errors.New(msg))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"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"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -202,7 +203,7 @@ type BlockChain interface {
|
||||||
// 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
|
||||||
// in the live key-value store.
|
// in the live key-value store.
|
||||||
InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error)
|
InsertReceiptChain(types.Blocks, []rlp.RawValue, uint64) (int, error)
|
||||||
|
|
||||||
// Snapshots returns the blockchain snapshot tree to paused it during sync.
|
// Snapshots returns the blockchain snapshot tree to paused it during sync.
|
||||||
Snapshots() *snapshot.Tree
|
Snapshots() *snapshot.Tree
|
||||||
|
|
@ -1034,7 +1035,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
|
||||||
"lastnumn", last.Number, "lasthash", last.Hash(),
|
"lastnumn", last.Number, "lasthash", last.Hash(),
|
||||||
)
|
)
|
||||||
blocks := make([]*types.Block, len(results))
|
blocks := make([]*types.Block, len(results))
|
||||||
receipts := make([]types.Receipts, len(results))
|
receipts := make([]rlp.RawValue, len(results))
|
||||||
for i, result := range results {
|
for i, result := range results {
|
||||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.body())
|
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.body())
|
||||||
receipts[i] = result.Receipts
|
receipts[i] = result.Receipts
|
||||||
|
|
@ -1051,7 +1052,7 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
||||||
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
|
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
|
||||||
|
|
||||||
// Commit the pivot block as the new head, will require full sync from here on
|
// Commit the pivot block as the new head, will require full sync from here on
|
||||||
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil {
|
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []rlp.RawValue{result.Receipts}, d.ancientLimit); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := d.blockchain.SnapSyncCommitHead(block.Hash()); err != nil {
|
if err := d.blockchain.SnapSyncCommitHead(block.Hash()); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -255,23 +255,24 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
||||||
// peer in the download tester. The returned function can be used to retrieve
|
// peer in the download tester. The returned function can be used to retrieve
|
||||||
// batches of block receipts from the particularly requested peer.
|
// batches of block receipts from the particularly requested peer.
|
||||||
func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
|
func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
|
||||||
blobs := eth.ServiceGetReceiptsQuery(dlp.chain, hashes)
|
blobs := eth.ServiceGetReceiptsQuery68(dlp.chain, hashes)
|
||||||
|
|
||||||
receipts := make([][]*types.Receipt, len(blobs))
|
receipts := make([]types.Receipts, len(blobs))
|
||||||
for i, blob := range blobs {
|
for i, blob := range blobs {
|
||||||
rlp.DecodeBytes(blob, &receipts[i])
|
rlp.DecodeBytes(blob, &receipts[i])
|
||||||
}
|
}
|
||||||
hasher := trie.NewStackTrie(nil)
|
hasher := trie.NewStackTrie(nil)
|
||||||
hashes = make([]common.Hash, len(receipts))
|
hashes = make([]common.Hash, len(receipts))
|
||||||
for i, receipt := range receipts {
|
for i, receipt := range receipts {
|
||||||
hashes[i] = types.DeriveSha(types.Receipts(receipt), hasher)
|
hashes[i] = types.DeriveSha(receipt, hasher)
|
||||||
}
|
}
|
||||||
req := ð.Request{
|
req := ð.Request{
|
||||||
Peer: dlp.id,
|
Peer: dlp.id,
|
||||||
}
|
}
|
||||||
|
resp := eth.ReceiptsRLPResponse(types.EncodeBlockReceiptLists(receipts))
|
||||||
res := ð.Response{
|
res := ð.Response{
|
||||||
Req: req,
|
Req: req,
|
||||||
Res: (*eth.ReceiptsResponse)(&receipts),
|
Res: &resp,
|
||||||
Meta: hashes,
|
Meta: hashes,
|
||||||
Time: 1,
|
Time: 1,
|
||||||
Done: make(chan error, 1), // Ignore the returned status
|
Done: make(chan error, 1), // Ignore the returned status
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ func (q *receiptQueue) request(peer *peerConnection, req *fetchRequest, resCh ch
|
||||||
// deliver is responsible for taking a generic response packet from the concurrent
|
// deliver is responsible for taking a generic response packet from the concurrent
|
||||||
// fetcher, unpacking the receipt data and delivering it to the downloader's queue.
|
// fetcher, unpacking the receipt data and delivering it to the downloader's queue.
|
||||||
func (q *receiptQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
|
func (q *receiptQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
|
||||||
receipts := *packet.Res.(*eth.ReceiptsResponse)
|
receipts := *packet.Res.(*eth.ReceiptsRLPResponse)
|
||||||
hashes := packet.Meta.([]common.Hash) // {receipt hashes}
|
hashes := packet.Meta.([]common.Hash) // {receipt hashes}
|
||||||
|
|
||||||
accepted, err := q.queue.DeliverReceipts(peer.id, receipts, hashes)
|
accepted, err := q.queue.DeliverReceipts(peer.id, receipts, hashes)
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue