fix merge conflict
This commit is contained in:
commit
b2b32e6ff6
|
@ -29,7 +29,7 @@ import (
|
|||
var (
|
||||
// ErrNoCode is returned by call and transact operations for which the requested
|
||||
// recipient contract to operate on does not exist in the state db or does not
|
||||
// have any code associated with it (i.e. suicided).
|
||||
// have any code associated with it (i.e. self-destructed).
|
||||
ErrNoCode = errors.New("no contract code at given address")
|
||||
|
||||
// ErrNoPendingState is raised when attempting to perform a pending state action
|
||||
|
|
|
@ -161,6 +161,7 @@ func TestAdjustTime(t *testing.T) {
|
|||
func TestNewAdjustTimeFail(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.blockchain.Stop()
|
||||
|
||||
// Create tx and send
|
||||
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||
|
|
|
@ -348,7 +348,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// requireLengthPrefix returns whether the type requires any sort of length
|
||||
// requiresLengthPrefix returns whether the type requires any sort of length
|
||||
// prefixing.
|
||||
func (t Type) requiresLengthPrefix() bool {
|
||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Minimum amount of time between cache reloads. This limit applies if the platform does
|
||||
|
@ -38,11 +39,10 @@ import (
|
|||
// exist yet, the code will attempt to create a watcher at most this often.
|
||||
const minReloadInterval = 2 * time.Second
|
||||
|
||||
type accountsByURL []accounts.Account
|
||||
|
||||
func (s accountsByURL) Len() int { return len(s) }
|
||||
func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
|
||||
func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
// byURL defines the sorting order for accounts.
|
||||
func byURL(a, b accounts.Account) bool {
|
||||
return a.URL.Cmp(b.URL) < 0
|
||||
}
|
||||
|
||||
// AmbiguousAddrError is returned when attempting to unlock
|
||||
// an address for which more than one file exists.
|
||||
|
@ -67,7 +67,7 @@ type accountCache struct {
|
|||
keydir string
|
||||
watcher *watcher
|
||||
mu sync.Mutex
|
||||
all accountsByURL
|
||||
all []accounts.Account
|
||||
byAddr map[common.Address][]accounts.Account
|
||||
throttle *time.Timer
|
||||
notify chan struct{}
|
||||
|
@ -194,7 +194,7 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
|
|||
default:
|
||||
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
|
||||
copy(err.Matches, matches)
|
||||
sort.Sort(accountsByURL(err.Matches))
|
||||
slices.SortFunc(err.Matches, byURL)
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -31,6 +30,7 @@ import (
|
|||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -203,7 +203,7 @@ func TestCacheAddDeleteOrder(t *testing.T) {
|
|||
// Check that the account list is sorted by filename.
|
||||
wantAccounts := make([]accounts.Account, len(accs))
|
||||
copy(wantAccounts, accs)
|
||||
sort.Sort(accountsByURL(wantAccounts))
|
||||
slices.SortFunc(wantAccounts, byURL)
|
||||
list := cache.accounts()
|
||||
if !reflect.DeepEqual(list, wantAccounts) {
|
||||
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -31,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var testSigData = make([]byte, 32)
|
||||
|
@ -424,7 +424,7 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
|
|||
for _, account := range live {
|
||||
liveList = append(liveList, account)
|
||||
}
|
||||
sort.Sort(accountsByURL(liveList))
|
||||
slices.SortFunc(liveList, byURL)
|
||||
for j, wallet := range wallets {
|
||||
if accs := wallet.Accounts(); len(accs) != 1 {
|
||||
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
||||
|
|
|
@ -624,7 +624,7 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
|
|||
return signed, nil
|
||||
}
|
||||
|
||||
// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
|
||||
// SignTextWithPassphrase implements accounts.Wallet, however signing arbitrary
|
||||
// data is not supported for Ledger wallets, so this method will always return
|
||||
// an error.
|
||||
func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
||||
|
|
|
@ -32,6 +32,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
|||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
DataGasUsed *hexutil.Uint64 `json:"dataGasUsed"`
|
||||
ExcessDataGas *hexutil.Uint64 `json:"excessDataGas"`
|
||||
}
|
||||
var enc ExecutableData
|
||||
enc.ParentHash = e.ParentHash
|
||||
|
@ -54,6 +56,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
}
|
||||
enc.Withdrawals = e.Withdrawals
|
||||
enc.DataGasUsed = (*hexutil.Uint64)(e.DataGasUsed)
|
||||
enc.ExcessDataGas = (*hexutil.Uint64)(e.ExcessDataGas)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
@ -75,6 +79,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
|||
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
DataGasUsed *hexutil.Uint64 `json:"dataGasUsed"`
|
||||
ExcessDataGas *hexutil.Uint64 `json:"excessDataGas"`
|
||||
}
|
||||
var dec ExecutableData
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
@ -142,5 +148,11 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
|||
if dec.Withdrawals != nil {
|
||||
e.Withdrawals = dec.Withdrawals
|
||||
}
|
||||
if dec.DataGasUsed != nil {
|
||||
e.DataGasUsed = (*uint64)(dec.DataGasUsed)
|
||||
}
|
||||
if dec.ExcessDataGas != nil {
|
||||
e.ExcessDataGas = (*uint64)(dec.ExcessDataGas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
|||
type ExecutionPayloadEnvelope struct {
|
||||
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||
}
|
||||
var enc ExecutionPayloadEnvelope
|
||||
enc.ExecutionPayload = e.ExecutionPayload
|
||||
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
||||
enc.BlobsBundle = e.BlobsBundle
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
@ -29,6 +31,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
|||
type ExecutionPayloadEnvelope struct {
|
||||
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||
}
|
||||
var dec ExecutionPayloadEnvelope
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
@ -42,5 +45,8 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
|||
return errors.New("missing required field 'blockValue' for ExecutionPayloadEnvelope")
|
||||
}
|
||||
e.BlockValue = (*big.Int)(dec.BlockValue)
|
||||
if dec.BlobsBundle != nil {
|
||||
e.BlobsBundle = dec.BlobsBundle
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
|
@ -61,6 +62,8 @@ type ExecutableData struct {
|
|||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
DataGasUsed *uint64 `json:"dataGasUsed"`
|
||||
ExcessDataGas *uint64 `json:"excessDataGas"`
|
||||
}
|
||||
|
||||
// JSON type overrides for executableData.
|
||||
|
@ -73,6 +76,8 @@ type executableDataMarshaling struct {
|
|||
ExtraData hexutil.Bytes
|
||||
LogsBloom hexutil.Bytes
|
||||
Transactions []hexutil.Bytes
|
||||
DataGasUsed *hexutil.Uint64
|
||||
ExcessDataGas *hexutil.Uint64
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
||||
|
@ -80,6 +85,13 @@ type executableDataMarshaling struct {
|
|||
type ExecutionPayloadEnvelope struct {
|
||||
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||
}
|
||||
|
||||
type BlobsBundleV1 struct {
|
||||
Commitments []hexutil.Bytes `json:"commitments"`
|
||||
Proofs []hexutil.Bytes `json:"proofs"`
|
||||
Blobs []hexutil.Bytes `json:"blobs"`
|
||||
}
|
||||
|
||||
// JSON type overrides for ExecutionPayloadEnvelope.
|
||||
|
@ -152,14 +164,15 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
|||
// ExecutableDataToBlock constructs a block from executable data.
|
||||
// It verifies that the following fields:
|
||||
//
|
||||
// len(extraData) <= 32
|
||||
// uncleHash = emptyUncleHash
|
||||
// difficulty = 0
|
||||
// len(extraData) <= 32
|
||||
// uncleHash = emptyUncleHash
|
||||
// difficulty = 0
|
||||
// if versionedHashes != nil, versionedHashes match to blob transactions
|
||||
//
|
||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||
// Withdrawals value will propagate through the returned block. Empty
|
||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||
func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash) (*types.Block, error) {
|
||||
txs, err := decodeTransactions(params.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -174,6 +187,18 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
|||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||
}
|
||||
var blobHashes []common.Hash
|
||||
for _, tx := range txs {
|
||||
blobHashes = append(blobHashes, tx.BlobHashes()...)
|
||||
}
|
||||
if len(blobHashes) != len(versionedHashes) {
|
||||
return nil, fmt.Errorf("invalid number of versionedHashes: %v blobHashes: %v", versionedHashes, blobHashes)
|
||||
}
|
||||
for i := 0; i < len(blobHashes); i++ {
|
||||
if blobHashes[i] != versionedHashes[i] {
|
||||
return nil, fmt.Errorf("invalid versionedHash at %v: %v blobHashes: %v", i, versionedHashes, blobHashes)
|
||||
}
|
||||
}
|
||||
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
|
||||
// ExecutableData before withdrawals are enabled by marshaling
|
||||
// Withdrawals as the json null value.
|
||||
|
@ -199,6 +224,8 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
|||
Extra: params.ExtraData,
|
||||
MixDigest: params.Random,
|
||||
WithdrawalsHash: withdrawalsRoot,
|
||||
ExcessDataGas: params.ExcessDataGas,
|
||||
DataGasUsed: params.DataGasUsed,
|
||||
}
|
||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||
if block.Hash() != params.BlockHash {
|
||||
|
@ -209,7 +236,7 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
|||
|
||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||
// fields from the given block. It assumes the given block is post-merge block.
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope {
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, blobs []kzg4844.Blob, commitments []kzg4844.Commitment, proofs []kzg4844.Proof) *ExecutionPayloadEnvelope {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
|
@ -226,8 +253,20 @@ func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadE
|
|||
Random: block.MixDigest(),
|
||||
ExtraData: block.Extra(),
|
||||
Withdrawals: block.Withdrawals(),
|
||||
DataGasUsed: block.DataGasUsed(),
|
||||
ExcessDataGas: block.ExcessDataGas(),
|
||||
}
|
||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees}
|
||||
blobsBundle := BlobsBundleV1{
|
||||
Commitments: make([]hexutil.Bytes, 0),
|
||||
Blobs: make([]hexutil.Bytes, 0),
|
||||
Proofs: make([]hexutil.Bytes, 0),
|
||||
}
|
||||
for i := range blobs {
|
||||
blobsBundle.Blobs = append(blobsBundle.Blobs, hexutil.Bytes(blobs[i][:]))
|
||||
blobsBundle.Commitments = append(blobsBundle.Commitments, hexutil.Bytes(commitments[i][:]))
|
||||
blobsBundle.Proofs = append(blobsBundle.Proofs, hexutil.Bytes(proofs[i][:]))
|
||||
}
|
||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &blobsBundle}
|
||||
}
|
||||
|
||||
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
# This file contains sha256 checksums of optional build dependencies.
|
||||
|
||||
e447b498cde50215c4f7619e5124b0fc4e25fb5d16ea47271c47f278e7aa763a go1.20.3.src.tar.gz
|
||||
c1e1161d6d859deb576e6cfabeb40e3d042ceb1c6f444f617c3c9d76269c3565 go1.20.3.darwin-amd64.tar.gz
|
||||
86b0ed0f2b2df50fa8036eea875d1cf2d76cefdacf247c44639a1464b7e36b95 go1.20.3.darwin-arm64.tar.gz
|
||||
340e80abd047c597fdc0f50a6cc59617f06c297d62f7fc77f4a0164e2da6f7aa go1.20.3.freebsd-386.tar.gz
|
||||
2169fcd8b6c94c5fbe07c0b470ccfb6001d343f6548ad49f3d9ab78e3b5753c7 go1.20.3.freebsd-amd64.tar.gz
|
||||
e12384311403f1389d14cc1c1295bfb4e0dd5ab919403b80da429f671a223507 go1.20.3.linux-386.tar.gz
|
||||
979694c2c25c735755bf26f4f45e19e64e4811d661dd07b8c010f7a8e18adfca go1.20.3.linux-amd64.tar.gz
|
||||
eb186529f13f901e7a2c4438a05c2cd90d74706aaa0a888469b2a4a617b6ee54 go1.20.3.linux-arm64.tar.gz
|
||||
b421e90469a83671641f81b6e20df6500f033e9523e89cbe7b7223704dd1035c go1.20.3.linux-armv6l.tar.gz
|
||||
943c89aa1624ea544a022b31e3d6e16a037200e436370bdd5fd67f3fa60be282 go1.20.3.linux-ppc64le.tar.gz
|
||||
126cf823a5634ef2544b866db107b9d351d3ea70d9e240b0bdcfb46f4dcae54b go1.20.3.linux-s390x.tar.gz
|
||||
37e9146e1f9d681cfcaa6fee6c7b890c44c64bc50228c9588f3c4231346d33bd go1.20.3.windows-386.zip
|
||||
143a2837821c7dbacf7744cbb1a8421c1f48307c6fdfaeffc5f8c2f69e1b7932 go1.20.3.windows-amd64.zip
|
||||
158cb159e00bc979f473e0f5b5a561613129c5e51067967b72b8e072e5a4db81 go1.20.3.windows-arm64.zip
|
||||
62ee5bc6fb55b8bae8f705e0cb8df86d6453626b4ecf93279e2867092e0b7f70 go1.20.6.src.tar.gz
|
||||
98a09c085b4c385abae7d35b9155195d5e584d14988347ac7f18e4cbe3b5ef3d go1.20.6.darwin-amd64.tar.gz
|
||||
1163be1998835a13f00dfc869a8e3cdebf86984ad41ff2fff43e35ac2a0d8344 go1.20.6.darwin-arm64.tar.gz
|
||||
3e6801d33a52a599af9c5258e8626e368d6c7ba958e1b6a468a9a3eecad25d8b go1.20.6.freebsd-386.tar.gz
|
||||
e0d35bb22fa792448b675368189519ca636dedd7af7f8a2fafca344e497eb70e go1.20.6.freebsd-amd64.tar.gz
|
||||
2e27c9db1defbf4d58e907f9843bf60a1ce229688f8463bf24d6a0a19dc949de go1.20.6.linux-386.tar.gz
|
||||
b945ae2bb5db01a0fb4786afde64e6fbab50b67f6fa0eb6cfa4924f16a7ff1eb go1.20.6.linux-amd64.tar.gz
|
||||
4e15ab37556e979181a1a1cc60f6d796932223a0f5351d7c83768b356f84429b go1.20.6.linux-arm64.tar.gz
|
||||
669902f5c8efefbd5d5fd078db01e34355af3693e48659b89593da7db367c488 go1.20.6.linux-armv6l.tar.gz
|
||||
a1b91a42a40bba54bfd5c96c23d72250e0c424038d0d2b5c7950b828b4905822 go1.20.6.linux-ppc64le.tar.gz
|
||||
c5ec315cc57edd646f66d7079b51d3717db5bbeae4c83a771b709761db73688d go1.20.6.linux-s390x.tar.gz
|
||||
315c49723f93295bfaff0c15179e2d6936fb1ffc6f92837321d0d608fe1b6b51 go1.20.6.windows-386.zip
|
||||
b67dd7f2b4589701e53c98e348e1b4d9a7c3536dc316941172b2f0b60ae4ce5f go1.20.6.windows-amd64.zip
|
||||
9027e52be386e779ef1a0c938994ee2361689496ac832100407238f5ed0fd82a go1.20.6.windows-arm64.zip
|
||||
|
||||
fba08acc4027f69f07cef48fbff70b8a7ecdfaa1c2aba9ad3fb31d60d9f5d4bc golangci-lint-1.51.1-darwin-amd64.tar.gz
|
||||
75b8f0ff3a4e68147156be4161a49d4576f1be37a0b506473f8c482140c1e7f2 golangci-lint-1.51.1-darwin-arm64.tar.gz
|
||||
|
|
|
@ -139,7 +139,7 @@ var (
|
|||
// This is the version of Go that will be downloaded by
|
||||
//
|
||||
// go run ci.go install -dlgo
|
||||
dlgoVersion = "1.20.3"
|
||||
dlgoVersion = "1.20.6"
|
||||
|
||||
// This is the version of Go that will be used to bootstrap the PPA builder.
|
||||
//
|
||||
|
|
|
@ -46,12 +46,13 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -152,13 +153,6 @@ func (i info) gpl() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// authors implements the sort.Interface for strings in case-insensitive mode.
|
||||
type authors []string
|
||||
|
||||
func (as authors) Len() int { return len(as) }
|
||||
func (as authors) Less(i, j int) bool { return strings.ToLower(as[i]) < strings.ToLower(as[j]) }
|
||||
func (as authors) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
|
||||
|
||||
func main() {
|
||||
var (
|
||||
files = getFiles()
|
||||
|
@ -299,7 +293,9 @@ func writeAuthors(files []string) {
|
|||
}
|
||||
}
|
||||
// Write sorted list of authors back to the file.
|
||||
sort.Sort(authors(list))
|
||||
slices.SortFunc(list, func(a, b string) bool {
|
||||
return strings.ToLower(a) < strings.ToLower(b)
|
||||
})
|
||||
content := new(bytes.Buffer)
|
||||
content.WriteString(authorsFileHeader)
|
||||
for _, a := range list {
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
@ -108,20 +109,18 @@ func main() {
|
|||
utils.Fatalf("-ListenUDP: %v", err)
|
||||
}
|
||||
|
||||
realaddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
if natm != nil {
|
||||
if !realaddr.IP.IsLoopback() {
|
||||
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||
}
|
||||
if ext, err := natm.ExternalIP(); err == nil {
|
||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
||||
db, _ := enode.OpenDB("")
|
||||
ln := enode.NewLocalNode(db, nodeKey)
|
||||
|
||||
listenerAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
if natm != nil && !listenerAddr.IP.IsLoopback() {
|
||||
natAddr := doPortMapping(natm, ln, listenerAddr)
|
||||
if natAddr != nil {
|
||||
listenerAddr = natAddr
|
||||
}
|
||||
}
|
||||
|
||||
printNotice(&nodeKey.PublicKey, *realaddr)
|
||||
|
||||
db, _ := enode.OpenDB("")
|
||||
ln := enode.NewLocalNode(db, nodeKey)
|
||||
printNotice(&nodeKey.PublicKey, *listenerAddr)
|
||||
cfg := discover.Config{
|
||||
PrivateKey: nodeKey,
|
||||
NetRestrict: restrictList,
|
||||
|
@ -148,3 +147,60 @@ func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) {
|
|||
fmt.Println("Note: you're using cmd/bootnode, a developer tool.")
|
||||
fmt.Println("We recommend using a regular node as bootstrap node for production deployments.")
|
||||
}
|
||||
|
||||
func doPortMapping(natm nat.Interface, ln *enode.LocalNode, addr *net.UDPAddr) *net.UDPAddr {
|
||||
const (
|
||||
protocol = "udp"
|
||||
name = "ethereum discovery"
|
||||
)
|
||||
newLogger := func(external int, internal int) log.Logger {
|
||||
return log.New("proto", protocol, "extport", external, "intport", internal, "interface", natm)
|
||||
}
|
||||
|
||||
var (
|
||||
intport = addr.Port
|
||||
extaddr = &net.UDPAddr{IP: addr.IP, Port: addr.Port}
|
||||
mapTimeout = nat.DefaultMapTimeout
|
||||
log = newLogger(addr.Port, intport)
|
||||
)
|
||||
addMapping := func() {
|
||||
// Get the external address.
|
||||
var err error
|
||||
extaddr.IP, err = natm.ExternalIP()
|
||||
if err != nil {
|
||||
log.Debug("Couldn't get external IP", "err", err)
|
||||
return
|
||||
}
|
||||
// Create the mapping.
|
||||
p, err := natm.AddMapping(protocol, extaddr.Port, intport, name, mapTimeout)
|
||||
if err != nil {
|
||||
log.Debug("Couldn't add port mapping", "err", err)
|
||||
return
|
||||
}
|
||||
if p != uint16(extaddr.Port) {
|
||||
extaddr.Port = int(p)
|
||||
log = newLogger(extaddr.Port, intport)
|
||||
log.Info("NAT mapped alternative port")
|
||||
} else {
|
||||
log.Info("NAT mapped port")
|
||||
}
|
||||
// Update IP/port information of the local node.
|
||||
ln.SetStaticIP(extaddr.IP)
|
||||
ln.SetFallbackUDP(extaddr.Port)
|
||||
}
|
||||
|
||||
// Perform mapping once, synchronously.
|
||||
log.Info("Attempting port mapping")
|
||||
addMapping()
|
||||
|
||||
// Refresh the mapping periodically.
|
||||
go func() {
|
||||
refresh := time.NewTimer(mapTimeout)
|
||||
for range refresh.C {
|
||||
addMapping()
|
||||
refresh.Reset(mapTimeout)
|
||||
}
|
||||
}()
|
||||
|
||||
return extaddr
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
@ -743,7 +744,7 @@ func signer(c *cli.Context) error {
|
|||
port := c.Int(rpcPortFlag.Name)
|
||||
|
||||
// start http server
|
||||
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.HTTPListenAddrFlag.Name), port)
|
||||
httpEndpoint := net.JoinHostPort(c.String(utils.HTTPListenAddrFlag.Name), fmt.Sprintf("%d", port))
|
||||
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not start RPC api: %v", err)
|
||||
|
|
|
@ -87,11 +87,11 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
|
|||
go c.runIterator(doneCh, it)
|
||||
}
|
||||
var (
|
||||
added uint64
|
||||
updated uint64
|
||||
skipped uint64
|
||||
recent uint64
|
||||
removed uint64
|
||||
added atomic.Uint64
|
||||
updated atomic.Uint64
|
||||
skipped atomic.Uint64
|
||||
recent atomic.Uint64
|
||||
removed atomic.Uint64
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
wg.Add(nthreads)
|
||||
|
@ -103,15 +103,15 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
|
|||
case n := <-c.ch:
|
||||
switch c.updateNode(n) {
|
||||
case nodeSkipIncompat:
|
||||
atomic.AddUint64(&skipped, 1)
|
||||
skipped.Add(1)
|
||||
case nodeSkipRecent:
|
||||
atomic.AddUint64(&recent, 1)
|
||||
recent.Add(1)
|
||||
case nodeRemoved:
|
||||
atomic.AddUint64(&removed, 1)
|
||||
removed.Add(1)
|
||||
case nodeAdded:
|
||||
atomic.AddUint64(&added, 1)
|
||||
added.Add(1)
|
||||
default:
|
||||
atomic.AddUint64(&updated, 1)
|
||||
updated.Add(1)
|
||||
}
|
||||
case <-c.closed:
|
||||
return
|
||||
|
@ -138,11 +138,11 @@ loop:
|
|||
break loop
|
||||
case <-statusTicker.C:
|
||||
log.Info("Crawling in progress",
|
||||
"added", atomic.LoadUint64(&added),
|
||||
"updated", atomic.LoadUint64(&updated),
|
||||
"removed", atomic.LoadUint64(&removed),
|
||||
"ignored(recent)", atomic.LoadUint64(&recent),
|
||||
"ignored(incompatible)", atomic.LoadUint64(&skipped))
|
||||
"added", added.Load(),
|
||||
"updated", updated.Load(),
|
||||
"removed", removed.Load(),
|
||||
"ignored(recent)", recent.Load(),
|
||||
"ignored(incompatible)", skipped.Load())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -33,6 +32,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -288,11 +288,11 @@ func makeDeletionChanges(records map[string]recordSet, keep map[string]string) [
|
|||
// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
|
||||
func sortChanges(changes []types.Change) {
|
||||
score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3}
|
||||
sort.Slice(changes, func(i, j int) bool {
|
||||
if changes[i].Action == changes[j].Action {
|
||||
return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name
|
||||
slices.SortFunc(changes, func(a, b types.Change) bool {
|
||||
if a.Action == b.Action {
|
||||
return *a.ResourceRecordSet.Name < *b.ResourceRecordSet.Name
|
||||
}
|
||||
return score[string(changes[i].Action)] < score[string(changes[j].Action)]
|
||||
return score[string(a.Action)] < score[string(b.Action)]
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -109,15 +109,13 @@ func setupGeth(stack *node.Node) error {
|
|||
}
|
||||
|
||||
backend, err := eth.New(stack, ðconfig.Config{
|
||||
Genesis: &chain.genesis,
|
||||
NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763
|
||||
DatabaseCache: 10,
|
||||
TrieCleanCache: 10,
|
||||
TrieCleanCacheJournal: "",
|
||||
TrieCleanCacheRejournal: 60 * time.Minute,
|
||||
TrieDirtyCache: 16,
|
||||
TrieTimeout: 60 * time.Minute,
|
||||
SnapshotCache: 10,
|
||||
Genesis: &chain.genesis,
|
||||
NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763
|
||||
DatabaseCache: 10,
|
||||
TrieCleanCache: 10,
|
||||
TrieDirtyCache: 16,
|
||||
TrieTimeout: 60 * time.Minute,
|
||||
SnapshotCache: 10,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -21,11 +21,11 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
const jsonIndent = " "
|
||||
|
@ -77,8 +77,8 @@ func (ns nodeSet) nodes() []*enode.Node {
|
|||
result = append(result, n.N)
|
||||
}
|
||||
// Sort by ID.
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return bytes.Compare(result[i].ID().Bytes(), result[j].ID().Bytes()) < 0
|
||||
slices.SortFunc(result, func(a, b *enode.Node) bool {
|
||||
return bytes.Compare(a.ID().Bytes(), b.ID().Bytes()) < 0
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
@ -103,8 +103,8 @@ func (ns nodeSet) topN(n int) nodeSet {
|
|||
for _, v := range ns {
|
||||
byscore = append(byscore, v)
|
||||
}
|
||||
sort.Slice(byscore, func(i, j int) bool {
|
||||
return byscore[i].Score >= byscore[j].Score
|
||||
slices.SortFunc(byscore, func(a, b nodeJSON) bool {
|
||||
return a.Score >= b.Score
|
||||
})
|
||||
result := make(nodeSet, n)
|
||||
for _, v := range byscore[:n] {
|
||||
|
|
|
@ -306,7 +306,7 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
|||
return inputData, nil
|
||||
}
|
||||
|
||||
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
|
||||
// dispatchBlock writes the output data to either stderr or stdout, or to the specified
|
||||
// files
|
||||
func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
|
||||
raw, _ := rlp.EncodeToBytes(block)
|
||||
|
|
|
@ -19,7 +19,6 @@ package t8ntool
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
@ -243,7 +242,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
if miningReward >= 0 {
|
||||
// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
|
||||
// where
|
||||
// - the coinbase suicided, or
|
||||
// - the coinbase self-destructed, or
|
||||
// - there are only 'bad' transactions, which aren't executed. In those cases,
|
||||
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
|
||||
var (
|
||||
|
@ -270,9 +269,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
statedb.AddBalance(w.Address, amount, state.BalanceChangeWithdrawal)
|
||||
}
|
||||
// Commit block
|
||||
root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
|
||||
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
|
||||
}
|
||||
execRs := &ExecutionResult{
|
||||
|
@ -291,6 +289,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
|
||||
execRs.WithdrawalsRoot = &h
|
||||
}
|
||||
// Re-create statedb instance with new root upon the updated database
|
||||
// for accessing latest states.
|
||||
statedb, err = state.New(root, statedb.Database(), nil)
|
||||
if err != nil {
|
||||
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
||||
}
|
||||
return statedb, execRs, nil
|
||||
}
|
||||
|
||||
|
@ -306,7 +310,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB
|
|||
}
|
||||
}
|
||||
// Commit and re-open to start with a clean state.
|
||||
root, _ := statedb.Commit(false)
|
||||
root, _ := statedb.Commit(0, false)
|
||||
statedb, _ = state.New(root, sdb, nil)
|
||||
return statedb
|
||||
}
|
||||
|
|
|
@ -280,8 +280,7 @@ func runCmd(ctx *cli.Context) error {
|
|||
output, leftOverGas, stats, err := timedExec(bench, execFunc)
|
||||
|
||||
if ctx.Bool(DumpFlag.Name) {
|
||||
statedb.Commit(true)
|
||||
statedb.IntermediateRoot(true)
|
||||
statedb.Commit(genesisConfig.Number, true)
|
||||
fmt.Println(string(statedb.Dump(nil)))
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
|||
// Test failed, mark as so and dump any state to aid debugging
|
||||
result.Pass, result.Error = false, err.Error()
|
||||
if dump && s != nil {
|
||||
s, _ = state.New(*result.Root, s.Database(), nil)
|
||||
dump := s.RawDump(nil)
|
||||
result.State = &dump
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
## Input transactions in RLP form
|
||||
|
||||
This testdata folder is used to examplify how transaction input can be provided in rlp form.
|
||||
This testdata folder is used to exemplify how transaction input can be provided in rlp form.
|
||||
Please see the README in `evm` folder for how this is performed.
|
|
@ -1 +1 @@
|
|||
These files examplify a selfdestruct to the `0`-address.
|
||||
These files exemplify a selfdestruct to the `0`-address.
|
|
@ -1 +1 @@
|
|||
These files examplify how to sign a transaction using the pre-EIP155 scheme.
|
||||
These files exemplify how to sign a transaction using the pre-EIP155 scheme.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
These files examplify a transition where a transaction (excuted on block 5) requests
|
||||
These files exemplify a transition where a transaction (executed on block 5) requests
|
||||
the blockhash for block `1`.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
These files examplify a transition where a transaction (excuted on block 5) requests
|
||||
These files exemplify a transition where a transaction (executed on block 5) requests
|
||||
the blockhash for block `4`, but where the hash for that block is missing.
|
||||
It's expected that executing these should cause `exit` with errorcode `4`.
|
||||
|
|
|
@ -1 +1 @@
|
|||
These files examplify a transition where there are no transcations, two ommers, at block `N-1` (delta 1) and `N-2` (delta 2).
|
||||
These files exemplify a transition where there are no transactions, two ommers, at block `N-1` (delta 1) and `N-2` (delta 2).
|
|
@ -7,7 +7,7 @@ This test contains testcases for EIP-2930, which uses transactions with access l
|
|||
The alloc portion contains one contract (`0x000000000000000000000000000000000000aaaa`), containing the
|
||||
following code: `0x5854505854`: `PC ;SLOAD; POP; PC; SLOAD`.
|
||||
|
||||
Essentialy, this contract does `SLOAD(0)` and `SLOAD(3)`.
|
||||
Essentially, this contract does `SLOAD(0)` and `SLOAD(3)`.
|
||||
|
||||
The alloc also contains some funds on `0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b`.
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ This test contains testcases for EIP-1559, which uses an new transaction type an
|
|||
The alloc portion contains one contract (`0x000000000000000000000000000000000000aaaa`), containing the
|
||||
following code: `0x58585454`: `PC; PC; SLOAD; SLOAD`.
|
||||
|
||||
Essentialy, this contract does `SLOAD(0)` and `SLOAD(1)`.
|
||||
Essentially, this contract does `SLOAD(0)` and `SLOAD(1)`.
|
||||
|
||||
The alloc also contains some funds on `0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b`.
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ func TestRemoteDbWithHeaders(t *testing.T) {
|
|||
}
|
||||
|
||||
func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) {
|
||||
var ok uint32
|
||||
var ok atomic.Uint32
|
||||
server := &http.Server{
|
||||
Addr: "localhost:0",
|
||||
Handler: &testHandler{func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -72,12 +72,12 @@ func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) {
|
|||
if have, want := r.Header.Get("second"), "two"; have != want {
|
||||
t.Fatalf("missing header, have %v want %v", have, want)
|
||||
}
|
||||
atomic.StoreUint32(&ok, 1)
|
||||
ok.Store(1)
|
||||
}}}
|
||||
go server.Serve(ln)
|
||||
defer server.Close()
|
||||
runGeth(t, gethArgs...).WaitExit()
|
||||
if atomic.LoadUint32(&ok) != 1 {
|
||||
if ok.Load() != 1 {
|
||||
t.Fatal("Test fail, expected invocation to succeed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,16 +262,16 @@ func importChain(ctx *cli.Context) error {
|
|||
defer db.Close()
|
||||
|
||||
// Start periodically gathering memory profiles
|
||||
var peakMemAlloc, peakMemSys uint64
|
||||
var peakMemAlloc, peakMemSys atomic.Uint64
|
||||
go func() {
|
||||
stats := new(runtime.MemStats)
|
||||
for {
|
||||
runtime.ReadMemStats(stats)
|
||||
if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc {
|
||||
atomic.StoreUint64(&peakMemAlloc, stats.Alloc)
|
||||
if peakMemAlloc.Load() < stats.Alloc {
|
||||
peakMemAlloc.Store(stats.Alloc)
|
||||
}
|
||||
if atomic.LoadUint64(&peakMemSys) < stats.Sys {
|
||||
atomic.StoreUint64(&peakMemSys, stats.Sys)
|
||||
if peakMemSys.Load() < stats.Sys {
|
||||
peakMemSys.Store(stats.Sys)
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
@ -304,8 +304,8 @@ func importChain(ctx *cli.Context) error {
|
|||
mem := new(runtime.MemStats)
|
||||
runtime.ReadMemStats(mem)
|
||||
|
||||
fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024)
|
||||
fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024)
|
||||
fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(peakMemAlloc.Load())/1024/1024)
|
||||
fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(peakMemSys.Load())/1024/1024)
|
||||
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
|
||||
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/accounts/scwallet"
|
||||
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
ethcatalyst "github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
|
@ -170,6 +172,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
|||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
cfg.Eth.OverrideCancun = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
cfg.Eth.OverrideVerkle = &v
|
||||
}
|
||||
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
// Configure log filter RPC API.
|
||||
|
@ -189,6 +195,22 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
|||
if ctx.IsSet(utils.SyncTargetFlag.Name) && cfg.Eth.SyncMode == downloader.FullSync {
|
||||
utils.RegisterFullSyncTester(stack, eth, ctx.Path(utils.SyncTargetFlag.Name))
|
||||
}
|
||||
|
||||
// Start the dev mode if requested, or launch the engine API for
|
||||
// interacting with external consensus client.
|
||||
if ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), eth)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to register dev mode catalyst service: %v", err)
|
||||
}
|
||||
catalyst.RegisterSimulatedBeaconAPIs(stack, simBeacon)
|
||||
stack.RegisterLifecycle(simBeacon)
|
||||
} else if cfg.Eth.SyncMode != downloader.LightSync {
|
||||
err := ethcatalyst.Register(stack, eth)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to register catalyst service: %v", err)
|
||||
}
|
||||
}
|
||||
return stack, backend
|
||||
}
|
||||
|
||||
|
@ -272,6 +294,10 @@ func deprecated(field string) bool {
|
|||
return true
|
||||
case "ethconfig.Config.EWASMInterpreter":
|
||||
return true
|
||||
case "ethconfig.Config.TrieCleanCacheJournal":
|
||||
return true
|
||||
case "ethconfig.Config.TrieCleanCacheRejournal":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -519,8 +519,12 @@ func dbDumpTrie(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trieIt, err := theTrie.NodeIterator(start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var count int64
|
||||
it := trie.NewIterator(theTrie.NodeIterator(start))
|
||||
it := trie.NewIterator(trieIt)
|
||||
for it.Next() {
|
||||
if max > 0 && count == max {
|
||||
fmt.Printf("Exiting after %d values\n", count)
|
||||
|
|
|
@ -107,10 +107,10 @@ func ipcEndpoint(ipcPath, datadir string) string {
|
|||
// but windows require pipes to sit in "\\.\pipe\". Therefore, to run several
|
||||
// nodes simultaneously, we need to distinguish between them, which we do by
|
||||
// the pipe filename instead of folder.
|
||||
var nextIPC = uint32(0)
|
||||
var nextIPC atomic.Uint32
|
||||
|
||||
func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
|
||||
ipcName := fmt.Sprintf("geth-%d.ipc", atomic.AddUint32(&nextIPC, 1))
|
||||
ipcName := fmt.Sprintf("geth-%d.ipc", nextIPC.Add(1))
|
||||
args = append([]string{"--networkid=42", "--port=0", "--authrpc.port", "0", "--ipcpath", ipcName}, args...)
|
||||
t.Logf("Starting %v with rpc: %v", name, args)
|
||||
|
||||
|
@ -146,13 +146,13 @@ func startLightServer(t *testing.T) *gethrpc {
|
|||
t.Logf("Importing keys to geth")
|
||||
runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit()
|
||||
account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
|
||||
server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--miner.etherbase=0x02f0d131f1f97aef08aec6e3291b957d9efe7105", "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4")
|
||||
server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--miner.etherbase=0x02f0d131f1f97aef08aec6e3291b957d9efe7105", "--mine", "--light.serve=100", "--light.maxpeers=1", "--discv4=false", "--nat=extip:127.0.0.1", "--verbosity=4")
|
||||
return server
|
||||
}
|
||||
|
||||
func startClient(t *testing.T, name string) *gethrpc {
|
||||
datadir := initGeth(t)
|
||||
return startGethWithIpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=4")
|
||||
return startGethWithIpc(t, name, "--datadir", datadir, "--discv4=false", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=4")
|
||||
}
|
||||
|
||||
func TestPriorityClient(t *testing.T) {
|
||||
|
|
|
@ -44,6 +44,9 @@ import (
|
|||
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
|
||||
_ "github.com/ethereum/go-ethereum/eth/tracers/native"
|
||||
|
||||
// Automatically set GOMAXPROCS to match Linux container CPU quota.
|
||||
_ "go.uber.org/automaxprocs"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
@ -65,6 +68,7 @@ var (
|
|||
utils.USBFlag,
|
||||
utils.SmartCardDaemonPathFlag,
|
||||
utils.OverrideCancun,
|
||||
utils.OverrideVerkle,
|
||||
utils.EnablePersonal,
|
||||
utils.TxPoolLocalsFlag,
|
||||
utils.TxPoolNoLocalsFlag,
|
||||
|
@ -89,9 +93,6 @@ var (
|
|||
utils.LightMaxPeersFlag,
|
||||
utils.LightNoPruneFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.UltraLightServersFlag,
|
||||
utils.UltraLightFractionFlag,
|
||||
utils.UltraLightOnlyAnnounceFlag,
|
||||
utils.LightNoSyncServeFlag,
|
||||
utils.EthRequiredBlocksFlag,
|
||||
utils.LegacyWhitelistFlag,
|
||||
|
@ -121,14 +122,16 @@ var (
|
|||
utils.MinerNewPayloadTimeout,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV4Flag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.LegacyDiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.DNSDiscoveryFlag,
|
||||
utils.DeveloperFlag,
|
||||
utils.DeveloperPeriodFlag,
|
||||
utils.DeveloperGasLimitFlag,
|
||||
utils.DeveloperPeriodFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.VMTraceFlag,
|
||||
utils.NetworkIdFlag,
|
||||
|
@ -408,7 +411,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
|
|||
}
|
||||
|
||||
// Start auxiliary services if enabled
|
||||
if ctx.Bool(utils.MiningEnabledFlag.Name) || ctx.Bool(utils.DeveloperFlag.Name) {
|
||||
if ctx.Bool(utils.MiningEnabledFlag.Name) {
|
||||
// Mining only makes sense if a full Ethereum node is running
|
||||
if ctx.String(utils.SyncModeFlag.Name) == "light" {
|
||||
utils.Fatalf("Light clients do not support mining")
|
||||
|
|
|
@ -50,7 +50,6 @@ var (
|
|||
ArgsUsage: "<root>",
|
||||
Action: pruneState,
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.CacheTrieJournalFlag,
|
||||
utils.BloomFilterSizeFlag,
|
||||
}, utils.NetworkFlags, utils.DatabasePathFlags),
|
||||
Description: `
|
||||
|
@ -160,7 +159,7 @@ block is used.
|
|||
// Deprecation: this command should be deprecated once the hash-based
|
||||
// scheme is deprecated.
|
||||
func pruneState(ctx *cli.Context) error {
|
||||
stack, config := makeConfigNode(ctx)
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, false)
|
||||
|
@ -168,7 +167,6 @@ func pruneState(ctx *cli.Context) error {
|
|||
|
||||
prunerconfig := pruner.Config{
|
||||
Datadir: stack.ResolvePath(""),
|
||||
Cachedir: stack.ResolvePath(config.Eth.TrieCleanCacheJournal),
|
||||
BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name),
|
||||
}
|
||||
pruner, err := pruner.NewPruner(chaindb, prunerconfig)
|
||||
|
@ -292,7 +290,12 @@ func traverseState(ctx *cli.Context) error {
|
|||
lastReport time.Time
|
||||
start = time.Now()
|
||||
)
|
||||
accIter := trie.NewIterator(t.NodeIterator(nil))
|
||||
acctIt, err := t.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open iterator", "root", root, "err", err)
|
||||
return err
|
||||
}
|
||||
accIter := trie.NewIterator(acctIt)
|
||||
for accIter.Next() {
|
||||
accounts += 1
|
||||
var acc types.StateAccount
|
||||
|
@ -307,7 +310,12 @@ func traverseState(ctx *cli.Context) error {
|
|||
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
storageIter := trie.NewIterator(storageTrie.NodeIterator(nil))
|
||||
storageIt, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
storageIter := trie.NewIterator(storageIt)
|
||||
for storageIter.Next() {
|
||||
slots += 1
|
||||
}
|
||||
|
@ -385,7 +393,11 @@ func traverseRawState(ctx *cli.Context) error {
|
|||
hasher = crypto.NewKeccakState()
|
||||
got = make([]byte, 32)
|
||||
)
|
||||
accIter := t.NodeIterator(nil)
|
||||
accIter, err := t.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open iterator", "root", root, "err", err)
|
||||
return err
|
||||
}
|
||||
for accIter.Next(true) {
|
||||
nodes += 1
|
||||
node := accIter.Hash()
|
||||
|
@ -422,7 +434,11 @@ func traverseRawState(ctx *cli.Context) error {
|
|||
log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
|
||||
return errors.New("missing storage trie")
|
||||
}
|
||||
storageIter := storageTrie.NodeIterator(nil)
|
||||
storageIter, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
|
||||
return err
|
||||
}
|
||||
for storageIter.Next(true) {
|
||||
nodes += 1
|
||||
node := storageIter.Hash()
|
||||
|
|
|
@ -74,7 +74,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
|
|||
switch node := root.(type) {
|
||||
case *verkle.InternalNode:
|
||||
for i, child := range node.Children() {
|
||||
childC := child.ComputeCommitment().Bytes()
|
||||
childC := child.Commit().Bytes()
|
||||
|
||||
childS, err := resolver(childC[:])
|
||||
if bytes.Equal(childC[:], zero[:]) {
|
||||
|
@ -86,7 +86,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
|
|||
// depth is set to 0, the tree isn't rebuilt so it's not a problem
|
||||
childN, err := verkle.ParseNode(childS, 0, childC[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode error child %x in db: %w", child.ComputeCommitment().Bytes(), err)
|
||||
return fmt.Errorf("decode error child %x in db: %w", child.Commitment().Bytes(), err)
|
||||
}
|
||||
if err := checkChildren(childN, resolver); err != nil {
|
||||
return fmt.Errorf("%x%w", i, err) // write the path to the erroring node
|
||||
|
@ -100,7 +100,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
|
|||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("Both balance and nonce are 0")
|
||||
return errors.New("both balance and nonce are 0")
|
||||
case verkle.Empty:
|
||||
// nothing to do
|
||||
default:
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -41,7 +42,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
@ -60,7 +61,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
lescatalyst "github.com/ethereum/go-ethereum/les/catalyst"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/metrics/exp"
|
||||
|
@ -159,7 +159,7 @@ var (
|
|||
Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
|
||||
Category: flags.DevCategory,
|
||||
}
|
||||
DeveloperPeriodFlag = &cli.IntFlag{
|
||||
DeveloperPeriodFlag = &cli.Uint64Flag{
|
||||
Name: "dev.period",
|
||||
Usage: "Block period to use in developer mode (0 = mine only if transaction pending)",
|
||||
Category: flags.DevCategory,
|
||||
|
@ -252,11 +252,6 @@ var (
|
|||
Usage: "Comma separated block number-to-hash mappings to require for peering (<number>=<hash>)",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
LegacyWhitelistFlag = &cli.StringFlag{
|
||||
Name: "whitelist",
|
||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>) (deprecated in favor of --eth.requiredblocks)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
BloomFilterSizeFlag = &cli.Uint64Flag{
|
||||
Name: "bloomfilter.size",
|
||||
Usage: "Megabytes of memory allocated to bloom-filter for pruning",
|
||||
|
@ -268,6 +263,11 @@ var (
|
|||
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideVerkle = &cli.Uint64Flag{
|
||||
Name: "override.verkle",
|
||||
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
// Light server and client settings
|
||||
LightServeFlag = &cli.IntFlag{
|
||||
Name: "light.serve",
|
||||
|
@ -293,23 +293,6 @@ var (
|
|||
Value: ethconfig.Defaults.LightPeers,
|
||||
Category: flags.LightCategory,
|
||||
}
|
||||
UltraLightServersFlag = &cli.StringFlag{
|
||||
Name: "ulc.servers",
|
||||
Usage: "List of trusted ultra-light servers",
|
||||
Value: strings.Join(ethconfig.Defaults.UltraLightServers, ","),
|
||||
Category: flags.LightCategory,
|
||||
}
|
||||
UltraLightFractionFlag = &cli.IntFlag{
|
||||
Name: "ulc.fraction",
|
||||
Usage: "Minimum % of trusted ultra-light servers required to announce a new head",
|
||||
Value: ethconfig.Defaults.UltraLightFraction,
|
||||
Category: flags.LightCategory,
|
||||
}
|
||||
UltraLightOnlyAnnounceFlag = &cli.BoolFlag{
|
||||
Name: "ulc.onlyannounce",
|
||||
Usage: "Ultra light server sends announcements only",
|
||||
Category: flags.LightCategory,
|
||||
}
|
||||
LightNoPruneFlag = &cli.BoolFlag{
|
||||
Name: "light.nopruning",
|
||||
Usage: "Disable ancient light chain data pruning",
|
||||
|
@ -334,18 +317,18 @@ var (
|
|||
TxPoolJournalFlag = &cli.StringFlag{
|
||||
Name: "txpool.journal",
|
||||
Usage: "Disk journal for local transaction to survive node restarts",
|
||||
Value: txpool.DefaultConfig.Journal,
|
||||
Value: ethconfig.Defaults.TxPool.Journal,
|
||||
Category: flags.TxPoolCategory,
|
||||
}
|
||||
TxPoolRejournalFlag = &cli.DurationFlag{
|
||||
Name: "txpool.rejournal",
|
||||
Usage: "Time interval to regenerate the local transaction journal",
|
||||
Value: txpool.DefaultConfig.Rejournal,
|
||||
Value: ethconfig.Defaults.TxPool.Rejournal,
|
||||
Category: flags.TxPoolCategory,
|
||||
}
|
||||
TxPoolPriceLimitFlag = &cli.Uint64Flag{
|
||||
Name: "txpool.pricelimit",
|
||||
Usage: "Minimum gas price limit to enforce for acceptance into the pool",
|
||||
Usage: "Minimum gas price tip to enforce for acceptance into the pool",
|
||||
Value: ethconfig.Defaults.TxPool.PriceLimit,
|
||||
Category: flags.TxPoolCategory,
|
||||
}
|
||||
|
@ -385,7 +368,6 @@ var (
|
|||
Value: ethconfig.Defaults.TxPool.Lifetime,
|
||||
Category: flags.TxPoolCategory,
|
||||
}
|
||||
|
||||
// Performance tuning settings
|
||||
CacheFlag = &cli.IntFlag{
|
||||
Name: "cache",
|
||||
|
@ -405,18 +387,6 @@ var (
|
|||
Value: 15,
|
||||
Category: flags.PerfCategory,
|
||||
}
|
||||
CacheTrieJournalFlag = &cli.StringFlag{
|
||||
Name: "cache.trie.journal",
|
||||
Usage: "Disk journal directory for trie cache to survive node restarts",
|
||||
Value: ethconfig.Defaults.TrieCleanCacheJournal,
|
||||
Category: flags.PerfCategory,
|
||||
}
|
||||
CacheTrieRejournalFlag = &cli.DurationFlag{
|
||||
Name: "cache.trie.rejournal",
|
||||
Usage: "Time interval to regenerate the trie cache journal",
|
||||
Value: ethconfig.Defaults.TrieCleanCacheRejournal,
|
||||
Category: flags.PerfCategory,
|
||||
}
|
||||
CacheGCFlag = &cli.IntFlag{
|
||||
Name: "cache.gc",
|
||||
Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)",
|
||||
|
@ -782,8 +752,16 @@ var (
|
|||
Usage: "Disables the peer discovery mechanism (manual peer addition)",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
DiscoveryV4Flag = &cli.BoolFlag{
|
||||
Name: "discovery.v4",
|
||||
Aliases: []string{"discv4"},
|
||||
Usage: "Enables the V4 discovery mechanism",
|
||||
Category: flags.NetworkingCategory,
|
||||
Value: true,
|
||||
}
|
||||
DiscoveryV5Flag = &cli.BoolFlag{
|
||||
Name: "v5disc",
|
||||
Name: "discovery.v5",
|
||||
Aliases: []string{"discv5"},
|
||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
|
@ -1220,19 +1198,6 @@ func setLes(ctx *cli.Context, cfg *ethconfig.Config) {
|
|||
if ctx.IsSet(LightMaxPeersFlag.Name) {
|
||||
cfg.LightPeers = ctx.Int(LightMaxPeersFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(UltraLightServersFlag.Name) {
|
||||
cfg.UltraLightServers = strings.Split(ctx.String(UltraLightServersFlag.Name), ",")
|
||||
}
|
||||
if ctx.IsSet(UltraLightFractionFlag.Name) {
|
||||
cfg.UltraLightFraction = ctx.Int(UltraLightFractionFlag.Name)
|
||||
}
|
||||
if cfg.UltraLightFraction <= 0 && cfg.UltraLightFraction > 100 {
|
||||
log.Error("Ultra light fraction is invalid", "had", cfg.UltraLightFraction, "updated", ethconfig.Defaults.UltraLightFraction)
|
||||
cfg.UltraLightFraction = ethconfig.Defaults.UltraLightFraction
|
||||
}
|
||||
if ctx.IsSet(UltraLightOnlyAnnounceFlag.Name) {
|
||||
cfg.UltraLightOnlyAnnounce = ctx.Bool(UltraLightOnlyAnnounceFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(LightNoPruneFlag.Name) {
|
||||
cfg.LightNoPrune = ctx.Bool(LightNoPruneFlag.Name)
|
||||
}
|
||||
|
@ -1373,13 +1338,17 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
|||
cfg.NoDiscovery = true
|
||||
}
|
||||
|
||||
// if we're running a light client or server, force enable the v5 peer discovery
|
||||
// unless it is explicitly disabled with --nodiscover note that explicitly specifying
|
||||
// --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
|
||||
forceV5Discovery := (lightClient || lightServer) && !ctx.Bool(NoDiscoverFlag.Name)
|
||||
if ctx.IsSet(DiscoveryV5Flag.Name) {
|
||||
cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name)
|
||||
} else if forceV5Discovery {
|
||||
// Disallow --nodiscover when used in conjunction with light mode.
|
||||
if (lightClient || lightServer) && ctx.Bool(NoDiscoverFlag.Name) {
|
||||
Fatalf("Cannot use --" + NoDiscoverFlag.Name + " in light client or light server mode")
|
||||
}
|
||||
CheckExclusive(ctx, DiscoveryV4Flag, NoDiscoverFlag)
|
||||
CheckExclusive(ctx, DiscoveryV5Flag, NoDiscoverFlag)
|
||||
cfg.DiscoveryV4 = ctx.Bool(DiscoveryV4Flag.Name)
|
||||
cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name)
|
||||
|
||||
// If we're running a light client or server, force enable the v5 peer discovery.
|
||||
if lightClient || lightServer {
|
||||
cfg.DiscoveryV5 = true
|
||||
}
|
||||
|
||||
|
@ -1505,7 +1474,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func setTxPool(ctx *cli.Context, cfg *txpool.Config) {
|
||||
func setTxPool(ctx *cli.Context, cfg *legacypool.Config) {
|
||||
if ctx.IsSet(TxPoolLocalsFlag.Name) {
|
||||
locals := strings.Split(ctx.String(TxPoolLocalsFlag.Name), ",")
|
||||
for _, account := range locals {
|
||||
|
@ -1710,12 +1679,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) {
|
||||
cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100
|
||||
}
|
||||
if ctx.IsSet(CacheTrieJournalFlag.Name) {
|
||||
cfg.TrieCleanCacheJournal = ctx.String(CacheTrieJournalFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(CacheTrieRejournalFlag.Name) {
|
||||
cfg.TrieCleanCacheRejournal = ctx.Duration(CacheTrieRejournalFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) {
|
||||
cfg.TrieDirtyCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100
|
||||
}
|
||||
|
@ -1838,7 +1801,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
log.Info("Using developer account", "address", developer.Address)
|
||||
|
||||
// Create a new developer genesis block or reuse existing one
|
||||
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.Int(DeveloperPeriodFlag.Name)), ctx.Uint64(DeveloperGasLimitFlag.Name), developer.Address)
|
||||
cfg.Genesis = core.DeveloperGenesisBlock(ctx.Uint64(DeveloperGasLimitFlag.Name), developer.Address)
|
||||
if ctx.IsSet(DataDirFlag.Name) {
|
||||
// If datadir doesn't exist we need to open db in write-mode
|
||||
// so leveldb can create files.
|
||||
|
@ -1898,9 +1861,6 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend
|
|||
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||
}
|
||||
stack.RegisterAPIs(tracers.APIs(backend.ApiBackend))
|
||||
if err := lescatalyst.Register(stack, backend); err != nil {
|
||||
Fatalf("Failed to register the Engine API service: %v", err)
|
||||
}
|
||||
return backend.ApiBackend, nil
|
||||
}
|
||||
backend, err := eth.New(stack, cfg)
|
||||
|
@ -1913,9 +1873,6 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend
|
|||
Fatalf("Failed to create the LES server: %v", err)
|
||||
}
|
||||
}
|
||||
if err := ethcatalyst.Register(stack, backend); err != nil {
|
||||
Fatalf("Failed to register the Engine API service: %v", err)
|
||||
}
|
||||
stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
|
||||
return backend.APIBackend, backend
|
||||
}
|
||||
|
@ -2018,7 +1975,7 @@ func SetupMetrics(ctx *cli.Context) {
|
|||
}
|
||||
|
||||
if ctx.IsSet(MetricsHTTPFlag.Name) {
|
||||
address := fmt.Sprintf("%s:%d", ctx.String(MetricsHTTPFlag.Name), ctx.Int(MetricsPortFlag.Name))
|
||||
address := net.JoinHostPort(ctx.String(MetricsHTTPFlag.Name), fmt.Sprintf("%d", ctx.Int(MetricsPortFlag.Name)))
|
||||
log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address)
|
||||
exp.Setup(address)
|
||||
} else if ctx.IsSet(MetricsPortFlag.Name) {
|
||||
|
|
|
@ -33,15 +33,41 @@ var ShowDeprecated = &cli.Command{
|
|||
|
||||
var DeprecatedFlags = []cli.Flag{
|
||||
NoUSBFlag,
|
||||
LegacyWhitelistFlag,
|
||||
CacheTrieJournalFlag,
|
||||
CacheTrieRejournalFlag,
|
||||
LegacyDiscoveryV5Flag,
|
||||
}
|
||||
|
||||
var (
|
||||
// (Deprecated May 2020, shown in aliased flags section)
|
||||
// Deprecated May 2020, shown in aliased flags section
|
||||
NoUSBFlag = &cli.BoolFlag{
|
||||
Name: "nousb",
|
||||
Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
// Deprecated March 2022
|
||||
LegacyWhitelistFlag = &cli.StringFlag{
|
||||
Name: "whitelist",
|
||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>) (deprecated in favor of --eth.requiredblocks)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
// Deprecated July 2023
|
||||
CacheTrieJournalFlag = &cli.StringFlag{
|
||||
Name: "cache.trie.journal",
|
||||
Usage: "Disk journal directory for trie cache to survive node restarts",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
CacheTrieRejournalFlag = &cli.DurationFlag{
|
||||
Name: "cache.trie.rejournal",
|
||||
Usage: "Time interval to regenerate the trie cache journal",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
LegacyDiscoveryV5Flag = &cli.BoolFlag{
|
||||
Name: "v5disc",
|
||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism (deprecated, use --discv5 instead)",
|
||||
Category: flags.DeprecatedCategory,
|
||||
}
|
||||
)
|
||||
|
||||
// showDeprecated displays deprecated flags that will be soon removed from the codebase.
|
||||
|
|
|
@ -170,6 +170,20 @@ func TestBasicLRUContains(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test that Peek doesn't update recent-ness
|
||||
func TestBasicLRUPeek(t *testing.T) {
|
||||
cache := NewBasicLRU[int, int](2)
|
||||
cache.Add(1, 1)
|
||||
cache.Add(2, 2)
|
||||
if v, ok := cache.Peek(1); !ok || v != 1 {
|
||||
t.Errorf("1 should be set to 1")
|
||||
}
|
||||
cache.Add(3, 3)
|
||||
if cache.Contains(1) {
|
||||
t.Errorf("should not have updated recent-ness of 1")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRU(b *testing.B) {
|
||||
var (
|
||||
capacity = 1000
|
||||
|
|
|
@ -82,7 +82,7 @@ func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
|
|||
// it however accepts either "0x"-prefixed (hex encoded) or non-prefixed (decimal)
|
||||
type Decimal256 big.Int
|
||||
|
||||
// NewHexOrDecimal256 creates a new Decimal256
|
||||
// NewDecimal256 creates a new Decimal256
|
||||
func NewDecimal256(x int64) *Decimal256 {
|
||||
b := big.NewInt(x)
|
||||
d := Decimal256(*b)
|
||||
|
|
|
@ -65,6 +65,11 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
|||
// If b is larger than len(h), b will be cropped from the left.
|
||||
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||
|
||||
// Less compares two hashes.
|
||||
func (h Hash) Less(other Hash) bool {
|
||||
return bytes.Compare(h[:], other[:]) < 0
|
||||
}
|
||||
|
||||
// Bytes gets the byte representation of the underlying hash.
|
||||
func (h Hash) Bytes() []byte { return h[:] }
|
||||
|
||||
|
@ -226,6 +231,11 @@ func IsHexAddress(s string) bool {
|
|||
return len(s) == 2*AddressLength && isHex(s)
|
||||
}
|
||||
|
||||
// Less compares two addresses.
|
||||
func (a Address) Less(other Address) bool {
|
||||
return bytes.Compare(a[:], other[:]) < 0
|
||||
}
|
||||
|
||||
// Bytes gets the string representation of the underlying address.
|
||||
func (a Address) Bytes() []byte { return a[:] }
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
// GetSigner returns the signer for a specific clique block.
|
||||
// Can be called with either a blocknumber, blockhash or an rlp encoded blob.
|
||||
// Can be called with a block number, a block hash or a rlp encoded blob.
|
||||
// The RLP encoded blob can either be a block or a header.
|
||||
func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) {
|
||||
if len(rlpOrBlockNr.RLP) == 0 {
|
||||
|
|
|
@ -19,7 +19,6 @@ package clique
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
@ -29,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Vote represents a single vote that an authorized signer made to modify the
|
||||
|
@ -62,13 +62,6 @@ type Snapshot struct {
|
|||
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
|
||||
}
|
||||
|
||||
// signersAscending implements the sort interface to allow sorting a list of addresses
|
||||
type signersAscending []common.Address
|
||||
|
||||
func (s signersAscending) Len() int { return len(s) }
|
||||
func (s signersAscending) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
|
||||
func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// newSnapshot creates a new snapshot with the specified startup parameters. This
|
||||
// method does not initialize the set of recent signers, so only ever use if for
|
||||
// the genesis block.
|
||||
|
@ -315,7 +308,7 @@ func (s *Snapshot) signers() []common.Address {
|
|||
for sig := range s.Signers {
|
||||
sigs = append(sigs, sig)
|
||||
}
|
||||
sort.Sort(signersAscending(sigs))
|
||||
slices.SortFunc(sigs, common.Address.Less)
|
||||
return sigs
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
@ -31,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// testerAccountPool is a pool to maintain currently active tester accounts,
|
||||
|
@ -53,7 +53,7 @@ func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string)
|
|||
for i, signer := range signers {
|
||||
auths[i] = ap.address(signer)
|
||||
}
|
||||
sort.Sort(signersAscending(auths))
|
||||
slices.SortFunc(auths, common.Address.Less)
|
||||
for i, auth := range auths {
|
||||
copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes())
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester {
|
|||
t.Fatalf("failed to create node: %v", err)
|
||||
}
|
||||
ethConf := ðconfig.Config{
|
||||
Genesis: core.DeveloperGenesisBlock(15, 11_500_000, common.Address{}),
|
||||
Genesis: core.DeveloperGenesisBlock(11_500_000, common.Address{}),
|
||||
Miner: miner.Config{
|
||||
Etherbase: common.HexToAddress(testAddress),
|
||||
},
|
||||
|
@ -111,6 +111,10 @@ func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester {
|
|||
t.Fatalf("failed to start test stack: %v", err)
|
||||
}
|
||||
client := stack.Attach()
|
||||
t.Cleanup(func() {
|
||||
client.Close()
|
||||
})
|
||||
|
||||
prompter := &hookedPrompter{scheduler: make(chan string)}
|
||||
printer := new(bytes.Buffer)
|
||||
|
||||
|
|
|
@ -86,18 +86,8 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
|||
for _, tx := range block.Transactions() {
|
||||
// Count the number of blobs to validate against the header's dataGasUsed
|
||||
blobs += len(tx.BlobHashes())
|
||||
|
||||
// Validate the data blobs individually too
|
||||
if tx.Type() == types.BlobTxType {
|
||||
if len(tx.BlobHashes()) == 0 {
|
||||
return errors.New("no-blob blob transaction present in block body")
|
||||
}
|
||||
for _, hash := range tx.BlobHashes() {
|
||||
if hash[0] != params.BlobTxHashVersion {
|
||||
return fmt.Errorf("blob hash version mismatch (have %d, supported %d)", hash[0], params.BlobTxHashVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
// The individual checks for blob validity (version-check + not empty)
|
||||
// happens in the state_transition check.
|
||||
}
|
||||
if header.DataGasUsed != nil {
|
||||
if want := *header.DataGasUsed / params.BlobTxDataGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated
|
||||
|
|
|
@ -18,7 +18,6 @@ package core
|
|||
|
||||
import (
|
||||
"math/big"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -235,118 +234,6 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// Tests that concurrent header verification works, for both good and bad blocks.
|
||||
func TestHeaderConcurrentVerification2(t *testing.T) { testHeaderConcurrentVerification(t, 2) }
|
||||
func TestHeaderConcurrentVerification8(t *testing.T) { testHeaderConcurrentVerification(t, 8) }
|
||||
func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) }
|
||||
|
||||
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
_, blocks, _ = GenerateChainWithGenesis(gspec, ethash.NewFaker(), 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Set the number of threads to verify on
|
||||
old := runtime.GOMAXPROCS(threads)
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Run the header checker for the entire block chain at once both for a valid and
|
||||
// also an invalid chain (enough if one arbitrary block is invalid).
|
||||
for i, valid := range []bool{true, false} {
|
||||
var results <-chan error
|
||||
|
||||
if valid {
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers)
|
||||
chain.Stop()
|
||||
} else {
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers)
|
||||
chain.Stop()
|
||||
}
|
||||
// Wait for all the verification results
|
||||
checks := make(map[int]error)
|
||||
for j := 0; j < len(blocks); j++ {
|
||||
select {
|
||||
case result := <-results:
|
||||
checks[j] = result
|
||||
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("test %d.%d: verification timeout", i, j)
|
||||
}
|
||||
}
|
||||
// Check nonce check validity
|
||||
for j := 0; j < len(blocks); j++ {
|
||||
want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail
|
||||
if (checks[j] == nil) != want {
|
||||
t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want)
|
||||
}
|
||||
if !want {
|
||||
// A few blocks after the first error may pass verification due to concurrent
|
||||
// workers. We don't care about those in this test, just that the correct block
|
||||
// errors out.
|
||||
break
|
||||
}
|
||||
}
|
||||
// Make sure no more data is returned
|
||||
select {
|
||||
case result := <-results:
|
||||
t.Fatalf("test %d: unexpected result returned: %v", i, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that aborting a header validation indeed prevents further checks from being
|
||||
// run, as well as checks that no left-over goroutines are leaked.
|
||||
func TestHeaderConcurrentAbortion2(t *testing.T) { testHeaderConcurrentAbortion(t, 2) }
|
||||
func TestHeaderConcurrentAbortion8(t *testing.T) { testHeaderConcurrentAbortion(t, 8) }
|
||||
func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) }
|
||||
|
||||
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
// Create a simple chain to verify
|
||||
var (
|
||||
gspec = &Genesis{Config: params.TestChainConfig}
|
||||
_, blocks, _ = GenerateChainWithGenesis(gspec, ethash.NewFaker(), 1024, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
headers[i] = block.Header()
|
||||
}
|
||||
// Set the number of threads to verify on
|
||||
old := runtime.GOMAXPROCS(threads)
|
||||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Start the verifications and immediately abort
|
||||
chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
abort, results := chain.engine.VerifyHeaders(chain, headers)
|
||||
close(abort)
|
||||
|
||||
// Deplete the results channel
|
||||
verified := 0
|
||||
for depleted := false; !depleted; {
|
||||
select {
|
||||
case result := <-results:
|
||||
if result != nil {
|
||||
t.Errorf("header %d: validation failed: %v", verified, result)
|
||||
}
|
||||
verified++
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
depleted = true
|
||||
}
|
||||
}
|
||||
// Check that abortion was honored by not processing too many POWs
|
||||
if verified > 2*threads {
|
||||
t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcGasLimit(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
pGasLimit uint64
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"io"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -48,6 +47,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -130,8 +130,6 @@ const (
|
|||
// that's resident in a blockchain.
|
||||
type CacheConfig struct {
|
||||
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
|
||||
TrieCleanJournal string // Disk journal for saving clean cache entries.
|
||||
TrieCleanRejournal time.Duration // Time interval to dump clean cache to disk periodically
|
||||
TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
|
||||
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
|
||||
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
|
||||
|
@ -251,7 +249,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
// Open trie database with provided config
|
||||
triedb := trie.NewDatabaseWithConfig(db, &trie.Config{
|
||||
Cache: cacheConfig.TrieCleanLimit,
|
||||
Journal: cacheConfig.TrieCleanJournal,
|
||||
Preimages: cacheConfig.Preimages,
|
||||
})
|
||||
var logger BlockchainLogger
|
||||
|
@ -370,7 +367,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
needRewind = true
|
||||
low = fullBlock.Number.Uint64()
|
||||
}
|
||||
// In fast sync, it may happen that ancient data has been written to the
|
||||
// In snap sync, it may happen that ancient data has been written to the
|
||||
// ancient store, but the LastFastBlock has not been updated, truncate the
|
||||
// extra data here.
|
||||
snapBlock := bc.CurrentSnapBlock()
|
||||
|
@ -434,18 +431,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
bc.wg.Add(1)
|
||||
go bc.updateFutureBlocks()
|
||||
|
||||
// If periodic cache journal is required, spin it up.
|
||||
if bc.cacheConfig.TrieCleanRejournal > 0 {
|
||||
if bc.cacheConfig.TrieCleanRejournal < time.Minute {
|
||||
log.Warn("Sanitizing invalid trie cache journal time", "provided", bc.cacheConfig.TrieCleanRejournal, "updated", time.Minute)
|
||||
bc.cacheConfig.TrieCleanRejournal = time.Minute
|
||||
}
|
||||
bc.wg.Add(1)
|
||||
go func() {
|
||||
defer bc.wg.Done()
|
||||
bc.triedb.SaveCachePeriodically(bc.cacheConfig.TrieCleanJournal, bc.cacheConfig.TrieCleanRejournal, bc.quit)
|
||||
}()
|
||||
}
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
|
@ -510,7 +495,7 @@ func (bc *BlockChain) loadLastState() error {
|
|||
}
|
||||
bc.hc.SetCurrentHeader(headHeader)
|
||||
|
||||
// Restore the last known head fast block
|
||||
// Restore the last known head snap block
|
||||
bc.currentSnapBlock.Store(headBlock.Header())
|
||||
headFastBlockGauge.Update(int64(headBlock.NumberU64()))
|
||||
|
||||
|
@ -545,21 +530,21 @@ func (bc *BlockChain) loadLastState() error {
|
|||
}
|
||||
log.Info("Loaded most recent local block", "number", headBlock.Number(), "hash", headBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(headBlock.Time()), 0)))
|
||||
if headBlock.Hash() != currentSnapBlock.Hash() {
|
||||
fastTd := bc.GetTd(currentSnapBlock.Hash(), currentSnapBlock.Number.Uint64())
|
||||
log.Info("Loaded most recent local snap block", "number", currentSnapBlock.Number, "hash", currentSnapBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(int64(currentSnapBlock.Time), 0)))
|
||||
snapTd := bc.GetTd(currentSnapBlock.Hash(), currentSnapBlock.Number.Uint64())
|
||||
log.Info("Loaded most recent local snap block", "number", currentSnapBlock.Number, "hash", currentSnapBlock.Hash(), "td", snapTd, "age", common.PrettyAge(time.Unix(int64(currentSnapBlock.Time), 0)))
|
||||
}
|
||||
if currentFinalBlock != nil {
|
||||
finalTd := bc.GetTd(currentFinalBlock.Hash(), currentFinalBlock.Number.Uint64())
|
||||
log.Info("Loaded most recent local finalized block", "number", currentFinalBlock.Number, "hash", currentFinalBlock.Hash(), "td", finalTd, "age", common.PrettyAge(time.Unix(int64(currentFinalBlock.Time), 0)))
|
||||
}
|
||||
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
|
||||
log.Info("Loaded last fast-sync pivot marker", "number", *pivot)
|
||||
log.Info("Loaded last snap-sync pivot marker", "number", *pivot)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHead rewinds the local chain to a new head. Depending on whether the node
|
||||
// was fast synced or full synced and in which state, the method will try to
|
||||
// was snap synced or full synced and in which state, the method will try to
|
||||
// delete minimal data from disk whilst retaining chain consistency.
|
||||
func (bc *BlockChain) SetHead(head uint64) error {
|
||||
if _, err := bc.setHeadBeyondRoot(head, 0, common.Hash{}, false); err != nil {
|
||||
|
@ -580,7 +565,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
|||
}
|
||||
|
||||
// SetHeadWithTimestamp rewinds the local chain to a new head that has at max
|
||||
// the given timestamp. Depending on whether the node was fast synced or full
|
||||
// the given timestamp. Depending on whether the node was snap synced or full
|
||||
// synced and in which state, the method will try to delete minimal data from
|
||||
// disk whilst retaining chain consistency.
|
||||
func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error {
|
||||
|
@ -626,7 +611,7 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
|
|||
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
||||
// that the rewind must pass the specified state root. This method is meant to be
|
||||
// used when rewinding with snapshots enabled to ensure that we go back further than
|
||||
// persistent disk layer. Depending on whether the node was fast synced or full, and
|
||||
// persistent disk layer. Depending on whether the node was snap synced or full, and
|
||||
// in which state, the method will try to delete minimal data from disk whilst
|
||||
// retaining chain consistency.
|
||||
//
|
||||
|
@ -714,7 +699,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||
bc.currentBlock.Store(newHeadBlock.Header())
|
||||
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
||||
}
|
||||
// Rewind the fast block in a simpleton way to the target head
|
||||
// Rewind the snap block in a simpleton way to the target head
|
||||
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
|
||||
newHeadSnapBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
|
||||
// If either blocks reached nil, reset to the genesis state
|
||||
|
@ -772,7 +757,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
|||
}
|
||||
} else {
|
||||
// Rewind the chain to the requested head and keep going backwards until a
|
||||
// block with a state is found or fast sync pivot is passed
|
||||
// block with a state is found or snap sync pivot is passed
|
||||
if time > 0 {
|
||||
log.Warn("Rewinding blockchain to timestamp", "target", time)
|
||||
bc.hc.SetHeadWithTimestamp(time, updateFn, delFn)
|
||||
|
@ -906,7 +891,7 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
|||
|
||||
// writeHeadBlock injects a new head block into the current block chain. This method
|
||||
// assumes that the block is indeed a true head. It will also reset the head
|
||||
// header and the head fast sync block to this very same block if they are older
|
||||
// header and the head snap sync block to this very same block if they are older
|
||||
// or if they are on a different side chain.
|
||||
//
|
||||
// Note, this function assumes that the `mu` mutex is held!
|
||||
|
@ -1010,11 +995,6 @@ func (bc *BlockChain) Stop() {
|
|||
if err := bc.stateCache.TrieDB().Close(); err != nil {
|
||||
log.Error("Failed to close trie db", "err", err)
|
||||
}
|
||||
// Ensure all live cached entries be saved into disk, so that we can skip
|
||||
// cache warmup when node restarts.
|
||||
if bc.cacheConfig.TrieCleanJournal != "" {
|
||||
bc.triedb.SaveCache(bc.cacheConfig.TrieCleanJournal)
|
||||
}
|
||||
log.Info("Blockchain stopped")
|
||||
}
|
||||
|
||||
|
@ -1038,8 +1018,8 @@ func (bc *BlockChain) procFutureBlocks() {
|
|||
}
|
||||
}
|
||||
if len(blocks) > 0 {
|
||||
sort.Slice(blocks, func(i, j int) bool {
|
||||
return blocks[i].NumberU64() < blocks[j].NumberU64()
|
||||
slices.SortFunc(blocks, func(a, b *types.Block) bool {
|
||||
return a.NumberU64() < b.NumberU64()
|
||||
})
|
||||
// Insert one by one as chain insertion needs contiguous ancestry between blocks
|
||||
for i := range blocks {
|
||||
|
@ -1092,7 +1072,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
size = int64(0)
|
||||
)
|
||||
|
||||
// updateHead updates the head fast sync block if the inserted blocks are better
|
||||
// updateHead updates the head snap sync block if the inserted blocks are better
|
||||
// and returns an indicator whether the inserted blocks are canonical.
|
||||
updateHead := func(head *types.Block) bool {
|
||||
if !bc.chainmu.TryLock() {
|
||||
|
@ -1191,7 +1171,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
if err := bc.db.Sync(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Update the current fast block because all block data is now present in DB.
|
||||
// Update the current snap block because all block data is now present in DB.
|
||||
previousSnapBlock := bc.CurrentSnapBlock().Number.Uint64()
|
||||
if !updateHead(blockChain[len(blockChain)-1]) {
|
||||
// We end up here if the header chain has reorg'ed, and the blocks/receipts
|
||||
|
@ -1379,7 +1359,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
log.Crit("Failed to write block into disk", "err", err)
|
||||
}
|
||||
// Commit all cached state changes into underlying memory database.
|
||||
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
|
||||
root, err := state.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1621,11 +1601,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
|||
block, err = it.next()
|
||||
}
|
||||
// The remaining blocks are still known blocks, the only scenario here is:
|
||||
// During the fast sync, the pivot point is already submitted but rollback
|
||||
// During the snap sync, the pivot point is already submitted but rollback
|
||||
// happens. Then node resets the head full block to a lower height via `rollback`
|
||||
// and leaves a few known blocks in the database.
|
||||
//
|
||||
// When node runs a fast sync again, it can re-import a batch of known blocks via
|
||||
// When node runs a snap sync again, it can re-import a batch of known blocks via
|
||||
// `insertChain` while a part of them have higher total difficulty than current
|
||||
// head full block(new pivot point).
|
||||
for block != nil && bc.skipBlock(err, it) {
|
||||
|
@ -2092,7 +2072,9 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
|
|||
// the processing of a block. These logs are later announced as deleted or reborn.
|
||||
func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
|
||||
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
|
||||
receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), b.Transactions())
|
||||
if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.Time(), b.BaseFee(), b.Transactions()); err != nil {
|
||||
log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err)
|
||||
}
|
||||
|
||||
var logs []*types.Log
|
||||
for _, receipt := range receipts {
|
||||
|
|
|
@ -306,9 +306,11 @@ func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
|
|||
// new code scheme.
|
||||
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
|
||||
type codeReader interface {
|
||||
ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error)
|
||||
ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error)
|
||||
}
|
||||
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
|
||||
// TODO(rjl493456442) The associated account address is also required
|
||||
// in Verkle scheme. Fix it once snap-sync is supported for Verkle.
|
||||
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Address{}, hash)
|
||||
}
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
|
|
|
@ -176,7 +176,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
blockchain.chainmu.MustLock()
|
||||
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash(), block.NumberU64()-1)))
|
||||
rawdb.WriteBlock(blockchain.db, block)
|
||||
statedb.Commit(false)
|
||||
statedb.Commit(block.NumberU64(), false)
|
||||
blockchain.chainmu.Unlock()
|
||||
}
|
||||
return nil
|
||||
|
@ -4129,6 +4129,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
// Import the blocks
|
||||
for _, block := range blocks {
|
||||
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
||||
|
@ -4215,6 +4216,7 @@ func TestTransientStorageReset(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
// Import the blocks
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to insert into chain: %v", err)
|
||||
|
@ -4309,6 +4311,7 @@ func TestEIP3651(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
defer chain.Stop()
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||
}
|
||||
|
||||
// Write state changes to db
|
||||
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
||||
root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
}
|
||||
|
|
|
@ -100,4 +100,8 @@ var (
|
|||
|
||||
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
||||
ErrSenderNoEOA = errors.New("sender not an eoa")
|
||||
|
||||
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
|
||||
// data gas fee of the block.
|
||||
ErrBlobFeeCapTooLow = errors.New("max fee per data gas less than block data gas fee")
|
||||
)
|
||||
|
|
21
core/evm.go
21
core/evm.go
|
@ -57,16 +57,17 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
random = &header.MixDigest
|
||||
}
|
||||
return vm.BlockContext{
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
Coinbase: beneficiary,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: header.Time,
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
Random: random,
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
Coinbase: beneficiary,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: header.Time,
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
Random: random,
|
||||
ExcessDataGas: header.ExcessDataGas,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,13 +24,13 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -270,8 +270,8 @@ func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) {
|
|||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(forksByBlock, func(i, j int) bool { return forksByBlock[i] < forksByBlock[j] })
|
||||
sort.Slice(forksByTime, func(i, j int) bool { return forksByTime[i] < forksByTime[j] })
|
||||
slices.Sort(forksByBlock)
|
||||
slices.Sort(forksByTime)
|
||||
|
||||
// Deduplicate fork identifiers applying multiple forks
|
||||
for i := 1; i < len(forksByBlock); i++ {
|
||||
|
|
|
@ -18,19 +18,21 @@ var _ = (*genesisSpecMarshaling)(nil)
|
|||
// MarshalJSON marshals as JSON.
|
||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessDataGas *math.HexOrDecimal64 `json:"excessDataGas"`
|
||||
DataGasUsed *math.HexOrDecimal64 `json:"dataGasUsed"`
|
||||
}
|
||||
var enc Genesis
|
||||
enc.Config = g.Config
|
||||
|
@ -51,25 +53,29 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
|
||||
enc.ParentHash = g.ParentHash
|
||||
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
|
||||
enc.ExcessDataGas = (*math.HexOrDecimal64)(g.ExcessDataGas)
|
||||
enc.DataGasUsed = (*math.HexOrDecimal64)(g.DataGasUsed)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
Coinbase *common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
Coinbase *common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessDataGas *math.HexOrDecimal64 `json:"excessDataGas"`
|
||||
DataGasUsed *math.HexOrDecimal64 `json:"dataGasUsed"`
|
||||
}
|
||||
var dec Genesis
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
@ -120,5 +126,11 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||
if dec.BaseFee != nil {
|
||||
g.BaseFee = (*big.Int)(dec.BaseFee)
|
||||
}
|
||||
if dec.ExcessDataGas != nil {
|
||||
g.ExcessDataGas = (*uint64)(dec.ExcessDataGas)
|
||||
}
|
||||
if dec.DataGasUsed != nil {
|
||||
g.DataGasUsed = (*uint64)(dec.DataGasUsed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -59,10 +59,12 @@ type Genesis struct {
|
|||
|
||||
// These fields are used for consensus tests. Please don't use them
|
||||
// in actual genesis blocks.
|
||||
Number uint64 `json:"number"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *big.Int `json:"baseFeePerGas"`
|
||||
Number uint64 `json:"number"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
||||
ExcessDataGas *uint64 `json:"excessDataGas"` // EIP-4844
|
||||
DataGasUsed *uint64 `json:"dataGasUsed"` // EIP-4844
|
||||
}
|
||||
|
||||
func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
||||
|
@ -96,6 +98,9 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
|||
genesis.Difficulty = genesisHeader.Difficulty
|
||||
genesis.Mixhash = genesisHeader.MixDigest
|
||||
genesis.Coinbase = genesisHeader.Coinbase
|
||||
genesis.BaseFee = genesisHeader.BaseFee
|
||||
genesis.ExcessDataGas = genesisHeader.ExcessDataGas
|
||||
genesis.DataGasUsed = genesisHeader.DataGasUsed
|
||||
|
||||
return &genesis, nil
|
||||
}
|
||||
|
@ -132,7 +137,7 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) {
|
|||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
return statedb.Commit(false)
|
||||
return statedb.Commit(0, false)
|
||||
}
|
||||
|
||||
// flush is very similar with deriveHash, but the main difference is
|
||||
|
@ -152,7 +157,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
|||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
root, err := statedb.Commit(false)
|
||||
root, err := statedb.Commit(0, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -215,15 +220,17 @@ type GenesisAccount struct {
|
|||
|
||||
// field type overrides for gencodec
|
||||
type genesisSpecMarshaling struct {
|
||||
Nonce math.HexOrDecimal64
|
||||
Timestamp math.HexOrDecimal64
|
||||
ExtraData hexutil.Bytes
|
||||
GasLimit math.HexOrDecimal64
|
||||
GasUsed math.HexOrDecimal64
|
||||
Number math.HexOrDecimal64
|
||||
Difficulty *math.HexOrDecimal256
|
||||
BaseFee *math.HexOrDecimal256
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
||||
Nonce math.HexOrDecimal64
|
||||
Timestamp math.HexOrDecimal64
|
||||
ExtraData hexutil.Bytes
|
||||
GasLimit math.HexOrDecimal64
|
||||
GasUsed math.HexOrDecimal64
|
||||
Number math.HexOrDecimal64
|
||||
Difficulty *math.HexOrDecimal256
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
||||
BaseFee *math.HexOrDecimal256
|
||||
ExcessDataGas *math.HexOrDecimal64
|
||||
DataGasUsed *math.HexOrDecimal64
|
||||
}
|
||||
|
||||
type genesisAccountMarshaling struct {
|
||||
|
@ -267,6 +274,7 @@ func (e *GenesisMismatchError) Error() string {
|
|||
// ChainOverrides contains the changes to chain config.
|
||||
type ChainOverrides struct {
|
||||
OverrideCancun *uint64
|
||||
OverrideVerkle *uint64
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
|
@ -295,6 +303,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
|
|||
if overrides != nil && overrides.OverrideCancun != nil {
|
||||
config.CancunTime = overrides.OverrideCancun
|
||||
}
|
||||
if overrides != nil && overrides.OverrideVerkle != nil {
|
||||
config.VerkleTime = overrides.OverrideVerkle
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
|
@ -460,9 +471,22 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||
}
|
||||
}
|
||||
var withdrawals []*types.Withdrawal
|
||||
if g.Config != nil && g.Config.IsShanghai(big.NewInt(int64(g.Number)), g.Timestamp) {
|
||||
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
if conf := g.Config; conf != nil {
|
||||
num := big.NewInt(int64(g.Number))
|
||||
if conf.IsShanghai(num, g.Timestamp) {
|
||||
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
if conf.IsCancun(num, g.Timestamp) {
|
||||
head.ExcessDataGas = g.ExcessDataGas
|
||||
head.DataGasUsed = g.DataGasUsed
|
||||
if head.ExcessDataGas == nil {
|
||||
head.ExcessDataGas = new(uint64)
|
||||
}
|
||||
if head.DataGasUsed == nil {
|
||||
head.DataGasUsed = new(uint64)
|
||||
}
|
||||
}
|
||||
}
|
||||
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals)
|
||||
}
|
||||
|
@ -554,21 +578,16 @@ func DefaultSepoliaGenesisBlock() *Genesis {
|
|||
}
|
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
|
||||
func DeveloperGenesisBlock(period uint64, gasLimit uint64, faucet common.Address) *Genesis {
|
||||
func DeveloperGenesisBlock(gasLimit uint64, faucet common.Address) *Genesis {
|
||||
// Override the default period to the user requested one
|
||||
config := *params.AllCliqueProtocolChanges
|
||||
config.Clique = ¶ms.CliqueConfig{
|
||||
Period: period,
|
||||
Epoch: config.Clique.Epoch,
|
||||
}
|
||||
config := *params.AllDevChainProtocolChanges
|
||||
|
||||
// Assemble and return the genesis with the precompiles and faucet pre-funded
|
||||
return &Genesis{
|
||||
Config: &config,
|
||||
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
|
||||
GasLimit: gasLimit,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: big.NewInt(1),
|
||||
Difficulty: big.NewInt(0),
|
||||
Alloc: map[common.Address]GenesisAccount{
|
||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||
|
|
|
@ -30,32 +30,28 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type allocItem struct{ Addr, Balance *big.Int }
|
||||
|
||||
type allocList []allocItem
|
||||
|
||||
func (a allocList) Len() int { return len(a) }
|
||||
func (a allocList) Less(i, j int) bool { return a[i].Addr.Cmp(a[j].Addr) < 0 }
|
||||
func (a allocList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func makelist(g *core.Genesis) allocList {
|
||||
a := make(allocList, 0, len(g.Alloc))
|
||||
func makelist(g *core.Genesis) []allocItem {
|
||||
items := make([]allocItem, 0, len(g.Alloc))
|
||||
for addr, account := range g.Alloc {
|
||||
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
|
||||
panic(fmt.Sprintf("can't encode account %x", addr))
|
||||
}
|
||||
bigAddr := new(big.Int).SetBytes(addr.Bytes())
|
||||
a = append(a, allocItem{bigAddr, account.Balance})
|
||||
items = append(items, allocItem{bigAddr, account.Balance})
|
||||
}
|
||||
sort.Sort(a)
|
||||
return a
|
||||
slices.SortFunc(items, func(a, b allocItem) bool {
|
||||
return a.Addr.Cmp(b.Addr) < 0
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
func makealloc(g *core.Genesis) string {
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -31,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
|
@ -836,23 +836,13 @@ type badBlock struct {
|
|||
Body *types.Body
|
||||
}
|
||||
|
||||
// badBlockList implements the sort interface to allow sorting a list of
|
||||
// bad blocks by their number in the reverse order.
|
||||
type badBlockList []*badBlock
|
||||
|
||||
func (s badBlockList) Len() int { return len(s) }
|
||||
func (s badBlockList) Less(i, j int) bool {
|
||||
return s[i].Header.Number.Uint64() < s[j].Header.Number.Uint64()
|
||||
}
|
||||
func (s badBlockList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// ReadBadBlock retrieves the bad block with the corresponding block hash.
|
||||
func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
|
||||
blob, err := db.Get(badBlockKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var badBlocks badBlockList
|
||||
var badBlocks []*badBlock
|
||||
if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -871,7 +861,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
|
|||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var badBlocks badBlockList
|
||||
var badBlocks []*badBlock
|
||||
if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -889,7 +879,7 @@ func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) {
|
|||
if err != nil {
|
||||
log.Warn("Failed to load old bad blocks", "error", err)
|
||||
}
|
||||
var badBlocks badBlockList
|
||||
var badBlocks []*badBlock
|
||||
if len(blob) > 0 {
|
||||
if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
|
||||
log.Crit("Failed to decode old bad blocks", "error", err)
|
||||
|
@ -905,7 +895,10 @@ func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) {
|
|||
Header: block.Header(),
|
||||
Body: block.Body(),
|
||||
})
|
||||
sort.Sort(sort.Reverse(badBlocks))
|
||||
slices.SortFunc(badBlocks, func(a, b *badBlock) bool {
|
||||
// Note: sorting in descending number order.
|
||||
return a.Header.Number.Uint64() >= b.Header.Number.Uint64()
|
||||
})
|
||||
if len(badBlocks) > badBlockToKeep {
|
||||
badBlocks = badBlocks[:badBlockToKeep]
|
||||
}
|
||||
|
|
|
@ -18,42 +18,18 @@ package rawdb
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"hash"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/blocktest"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// testHasher is the helper tool for transaction/receipt list hashing.
|
||||
// The original hasher is trie, in order to get rid of import cycle,
|
||||
// use the testing hasher instead.
|
||||
type testHasher struct {
|
||||
hasher hash.Hash
|
||||
}
|
||||
|
||||
func newHasher() *testHasher {
|
||||
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
|
||||
}
|
||||
|
||||
func (h *testHasher) Reset() {
|
||||
h.hasher.Reset()
|
||||
}
|
||||
|
||||
func (h *testHasher) Update(key, val []byte) error {
|
||||
h.hasher.Write(key)
|
||||
h.hasher.Write(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *testHasher) Hash() common.Hash {
|
||||
return common.BytesToHash(h.hasher.Sum(nil))
|
||||
}
|
||||
var newHasher = blocktest.NewHasher
|
||||
|
||||
// Tests that positional lookup metadata can be stored and retrieved.
|
||||
func TestLookupStorage(t *testing.T) {
|
||||
|
|
|
@ -19,12 +19,12 @@ package rawdb
|
|||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func TestChainIterator(t *testing.T) {
|
||||
|
@ -92,9 +92,11 @@ func TestChainIterator(t *testing.T) {
|
|||
}
|
||||
}
|
||||
if !c.reverse {
|
||||
sort.Ints(numbers)
|
||||
slices.Sort(numbers)
|
||||
} else {
|
||||
sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
|
||||
slices.SortFunc(numbers, func(a, b int) bool {
|
||||
return a > b // Sort descending
|
||||
})
|
||||
}
|
||||
if !reflect.DeepEqual(numbers, c.expect) {
|
||||
t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers)
|
||||
|
|
|
@ -197,9 +197,10 @@ func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
|||
|
||||
// AncientRange retrieves multiple items in sequence, starting from the index 'start'.
|
||||
// It will return
|
||||
// - at most 'max' items,
|
||||
// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
|
||||
// return as many items as fit into maxByteSize.
|
||||
// - at most 'count' items,
|
||||
// - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize),
|
||||
// but will otherwise return as many items as fit into maxByteSize.
|
||||
// - if maxBytes is not specified, 'count' items will be returned if they are present.
|
||||
func (f *Freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
if table := f.tables[kind]; table != nil {
|
||||
return table.RetrieveItems(start, count, maxBytes)
|
||||
|
|
|
@ -712,7 +712,7 @@ func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, e
|
|||
if !t.noCompression {
|
||||
decompressedSize, _ = snappy.DecodedLen(item)
|
||||
}
|
||||
if i > 0 && uint64(outputSize+decompressedSize) > maxBytes {
|
||||
if i > 0 && maxBytes != 0 && uint64(outputSize+decompressedSize) > maxBytes {
|
||||
break
|
||||
}
|
||||
if !t.noCompression {
|
||||
|
@ -730,8 +730,10 @@ func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, e
|
|||
}
|
||||
|
||||
// retrieveItems reads up to 'count' items from the table. It reads at least
|
||||
// one item, but otherwise avoids reading more than maxBytes bytes.
|
||||
// It returns the (potentially compressed) data, and the sizes.
|
||||
// one item, but otherwise avoids reading more than maxBytes bytes. Freezer
|
||||
// will ignore the size limitation and continuously allocate memory to store
|
||||
// data if maxBytes is 0. It returns the (potentially compressed) data, and
|
||||
// the sizes.
|
||||
func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []int, error) {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
@ -752,25 +754,22 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
|||
if start+count > items {
|
||||
count = items - start
|
||||
}
|
||||
var (
|
||||
output = make([]byte, maxBytes) // Buffer to read data into
|
||||
outputSize int // Used size of that buffer
|
||||
)
|
||||
var output []byte // Buffer to read data into
|
||||
if maxBytes != 0 {
|
||||
output = make([]byte, 0, maxBytes)
|
||||
} else {
|
||||
output = make([]byte, 0, 1024) // initial buffer cap
|
||||
}
|
||||
// readData is a helper method to read a single data item from disk.
|
||||
readData := func(fileId, start uint32, length int) error {
|
||||
// In case a small limit is used, and the elements are large, may need to
|
||||
// realloc the read-buffer when reading the first (and only) item.
|
||||
if len(output) < length {
|
||||
output = make([]byte, length)
|
||||
}
|
||||
output = grow(output, length)
|
||||
dataFile, exist := t.files[fileId]
|
||||
if !exist {
|
||||
return fmt.Errorf("missing data file %d", fileId)
|
||||
}
|
||||
if _, err := dataFile.ReadAt(output[outputSize:outputSize+length], int64(start)); err != nil {
|
||||
if _, err := dataFile.ReadAt(output[len(output)-length:], int64(start)); err != nil {
|
||||
return err
|
||||
}
|
||||
outputSize += length
|
||||
return nil
|
||||
}
|
||||
// Read all the indexes in one go
|
||||
|
@ -801,7 +800,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
|||
}
|
||||
readStart = 0
|
||||
}
|
||||
if i > 0 && uint64(totalSize+size) > maxBytes {
|
||||
if i > 0 && uint64(totalSize+size) > maxBytes && maxBytes != 0 {
|
||||
// About to break out due to byte limit being exceeded. We don't
|
||||
// read this last item, but we need to do the deferred reads now.
|
||||
if unreadSize > 0 {
|
||||
|
@ -815,7 +814,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
|||
unreadSize += size
|
||||
totalSize += size
|
||||
sizes = append(sizes, size)
|
||||
if i == len(indices)-2 || uint64(totalSize) > maxBytes {
|
||||
if i == len(indices)-2 || (uint64(totalSize) > maxBytes && maxBytes != 0) {
|
||||
// Last item, need to do the read now
|
||||
if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -826,7 +825,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i
|
|||
|
||||
// Update metrics.
|
||||
t.readMeter.Mark(int64(totalSize))
|
||||
return output[:outputSize], sizes, nil
|
||||
return output, sizes, nil
|
||||
}
|
||||
|
||||
// has returns an indicator whether the specified number data is still accessible
|
||||
|
|
|
@ -994,6 +994,52 @@ func TestSequentialReadByteLimit(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestSequentialReadNoByteLimit tests the batch-read if maxBytes is not specified.
|
||||
// Freezer should return the requested items regardless the size limitation.
|
||||
func TestSequentialReadNoByteLimit(t *testing.T) {
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("batchread-3-%d", rand.Uint64())
|
||||
{ // Fill table
|
||||
f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Write 10 bytes 30 times,
|
||||
// Splitting it at every 100 bytes (10 items)
|
||||
writeChunks(t, f, 30, 10)
|
||||
f.Close()
|
||||
}
|
||||
for i, tc := range []struct {
|
||||
items uint64
|
||||
want int
|
||||
}{
|
||||
{1, 1},
|
||||
{30, 30},
|
||||
{31, 30},
|
||||
} {
|
||||
{
|
||||
f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, tc.items, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), tc.want; have != want {
|
||||
t.Fatalf("test %d: want %d items, have %d ", i, want, have)
|
||||
}
|
||||
for ii, have := range items {
|
||||
want := getChunk(10, ii)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("test %d: data corruption item %d: have\n%x\n, want \n%x\n", i, ii, have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreezerReadonly(t *testing.T) {
|
||||
tmpdir := os.TempDir()
|
||||
// Case 1: Check it fails on non-existent file.
|
||||
|
|
|
@ -117,3 +117,19 @@ func truncateFreezerFile(file *os.File, size int64) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// grow prepares the slice space for new item, and doubles the slice capacity
|
||||
// if space is not enough.
|
||||
func grow(buf []byte, n int) []byte {
|
||||
if cap(buf)-len(buf) < n {
|
||||
newcap := 2 * cap(buf)
|
||||
if newcap-len(buf) < n {
|
||||
newcap = len(buf) + n
|
||||
}
|
||||
nbuf := make([]byte, len(buf), newcap)
|
||||
copy(nbuf, buf)
|
||||
buf = nbuf
|
||||
}
|
||||
buf = buf[:len(buf)+n]
|
||||
return buf
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
|
@ -43,16 +44,16 @@ type Database interface {
|
|||
OpenTrie(root common.Hash) (Trie, error)
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error)
|
||||
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error)
|
||||
|
||||
// CopyTrie returns an independent copy of the given trie.
|
||||
CopyTrie(Trie) Trie
|
||||
|
||||
// ContractCode retrieves a particular contract's code.
|
||||
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
|
||||
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
|
||||
ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
||||
|
||||
// DiskDB returns the underlying key-value disk database.
|
||||
DiskDB() ethdb.KeyValueStore
|
||||
|
@ -93,6 +94,10 @@ type Trie interface {
|
|||
// in the trie with provided address.
|
||||
UpdateAccount(address common.Address, account *types.StateAccount) error
|
||||
|
||||
// UpdateContractCode abstracts code write to the trie. It is expected
|
||||
// to be moved to the stateWriter interface when the latter is ready.
|
||||
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
|
||||
|
||||
// DeleteStorage removes any existing value for key from the trie. If a node
|
||||
// was not found in the database, a trie.MissingNodeError is returned.
|
||||
DeleteStorage(addr common.Address, key []byte) error
|
||||
|
@ -110,11 +115,12 @@ type Trie interface {
|
|||
// The returned nodeset can be nil if the trie is clean(nothing to commit).
|
||||
// Once the trie is committed, it's not usable anymore. A new trie must
|
||||
// be created with new root and updated trie database for following usage
|
||||
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet)
|
||||
Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error)
|
||||
|
||||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
|
||||
// starts at the key after the given start key.
|
||||
NodeIterator(startKey []byte) trie.NodeIterator
|
||||
// starts at the key after the given start key. And error will be returned
|
||||
// if fails to create node iterator.
|
||||
NodeIterator(startKey []byte) (trie.NodeIterator, error)
|
||||
|
||||
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
|
||||
// on the path to the value at key. The value itself is also included in the last
|
||||
|
@ -123,7 +129,7 @@ type Trie interface {
|
|||
// If the trie does not contain a value for key, the returned proof contains all
|
||||
// nodes of the longest existing prefix of the key (at least the root), ending
|
||||
// with the node that proves the absence of the key.
|
||||
Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error
|
||||
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
|
||||
}
|
||||
|
||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||
|
@ -172,8 +178,8 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
|||
}
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, addrHash, root), db.triedb)
|
||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -191,7 +197,7 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
|
|||
}
|
||||
|
||||
// ContractCode retrieves a particular contract's code.
|
||||
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
|
||||
func (db *cachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
code, _ := db.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
return code, nil
|
||||
|
@ -208,7 +214,7 @@ func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error
|
|||
// ContractCodeWithPrefix retrieves a particular contract's code. If the
|
||||
// code can't be found in the cache, then check the existence with **new**
|
||||
// db scheme.
|
||||
func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) {
|
||||
func (db *cachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
code, _ := db.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
return code, nil
|
||||
|
@ -223,11 +229,11 @@ func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]b
|
|||
}
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
|
||||
func (db *cachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
||||
return cached, nil
|
||||
}
|
||||
code, err := db.ContractCode(addrHash, codeHash)
|
||||
code, err := db.ContractCode(addr, codeHash)
|
||||
return len(code), err
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,11 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
|||
log.Info("Trie dumping started", "root", s.trie.Hash())
|
||||
c.OnRoot(s.trie.Hash())
|
||||
|
||||
it := trie.NewIterator(s.trie.NodeIterator(conf.Start))
|
||||
trieIt, err := s.trie.NodeIterator(conf.Start)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
it := trie.NewIterator(trieIt)
|
||||
for it.Next() {
|
||||
var data types.StateAccount
|
||||
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||
|
@ -167,18 +171,23 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
|||
} else {
|
||||
address = &addr
|
||||
}
|
||||
obj := newObject(s, addr, data)
|
||||
obj := newObject(s, addr, &data)
|
||||
if !conf.SkipCode {
|
||||
account.Code = obj.Code(s.db)
|
||||
account.Code = obj.Code()
|
||||
}
|
||||
if !conf.SkipStorage {
|
||||
account.Storage = make(map[common.Hash]string)
|
||||
tr, err := obj.getTrie(s.db)
|
||||
tr, err := obj.getTrie()
|
||||
if err != nil {
|
||||
log.Error("Failed to load storage trie", "err", err)
|
||||
continue
|
||||
}
|
||||
storageIt := trie.NewIterator(tr.NodeIterator(nil))
|
||||
trieIt, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to create trie iterator", "err", err)
|
||||
continue
|
||||
}
|
||||
storageIt := trie.NewIterator(trieIt)
|
||||
for storageIt.Next() {
|
||||
_, content, _, err := rlp.Split(storageIt.Value)
|
||||
if err != nil {
|
||||
|
|
|
@ -18,6 +18,7 @@ package state
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
@ -27,7 +28,8 @@ import (
|
|||
)
|
||||
|
||||
// nodeIterator is an iterator to traverse the entire state trie post-order,
|
||||
// including all of the contract code and contract state tries.
|
||||
// including all of the contract code and contract state tries. Preimage is
|
||||
// required in order to resolve the contract address.
|
||||
type nodeIterator struct {
|
||||
state *StateDB // State being iterated
|
||||
|
||||
|
@ -74,8 +76,12 @@ func (it *nodeIterator) step() error {
|
|||
return nil
|
||||
}
|
||||
// Initialize the iterator if we've just started
|
||||
var err error
|
||||
if it.stateIt == nil {
|
||||
it.stateIt = it.state.trie.NodeIterator(nil)
|
||||
it.stateIt, err = it.state.trie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If we had data nodes previously, we surely have at least state nodes
|
||||
if it.dataIt != nil {
|
||||
|
@ -109,18 +115,28 @@ func (it *nodeIterator) step() error {
|
|||
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil {
|
||||
return err
|
||||
}
|
||||
dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, common.BytesToHash(it.stateIt.LeafKey()), account.Root)
|
||||
// Lookup the preimage of account hash
|
||||
preimage := it.state.trie.GetKey(it.stateIt.LeafKey())
|
||||
if preimage == nil {
|
||||
return errors.New("account address is not available")
|
||||
}
|
||||
address := common.BytesToAddress(preimage)
|
||||
|
||||
// Traverse the storage slots belong to the account
|
||||
dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it.dataIt, err = dataTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it.dataIt = dataTrie.NodeIterator(nil)
|
||||
if !it.dataIt.Next(true) {
|
||||
it.dataIt = nil
|
||||
}
|
||||
if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||
it.codeHash = common.BytesToHash(account.CodeHash)
|
||||
addrHash := common.BytesToHash(it.stateIt.LeafKey())
|
||||
it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash))
|
||||
it.code, err = it.state.db.ContractCode(address, common.BytesToHash(account.CodeHash))
|
||||
if err != nil {
|
||||
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
||||
}
|
||||
|
|
|
@ -95,10 +95,14 @@ type (
|
|||
prevdestruct bool
|
||||
prevAccount []byte
|
||||
prevStorage map[common.Hash][]byte
|
||||
|
||||
prevAccountOriginExist bool
|
||||
prevAccountOrigin []byte
|
||||
prevStorageOrigin map[common.Hash][]byte
|
||||
}
|
||||
suicideChange struct {
|
||||
selfDestructChange struct {
|
||||
account *common.Address
|
||||
prev bool // whether account had already suicided
|
||||
prev bool // whether account had already self-destructed
|
||||
prevbalance *big.Int
|
||||
}
|
||||
|
||||
|
@ -163,10 +167,16 @@ func (ch resetObjectChange) revert(s *StateDB) {
|
|||
delete(s.stateObjectsDestruct, ch.prev.address)
|
||||
}
|
||||
if ch.prevAccount != nil {
|
||||
s.snapAccounts[ch.prev.addrHash] = ch.prevAccount
|
||||
s.accounts[ch.prev.addrHash] = ch.prevAccount
|
||||
}
|
||||
if ch.prevStorage != nil {
|
||||
s.snapStorage[ch.prev.addrHash] = ch.prevStorage
|
||||
s.storages[ch.prev.addrHash] = ch.prevStorage
|
||||
}
|
||||
if ch.prevAccountOriginExist {
|
||||
s.accountsOrigin[ch.prev.addrHash] = ch.prevAccountOrigin
|
||||
}
|
||||
if ch.prevStorageOrigin != nil {
|
||||
s.storagesOrigin[ch.prev.addrHash] = ch.prevStorageOrigin
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,15 +184,15 @@ func (ch resetObjectChange) dirtied() *common.Address {
|
|||
return ch.account
|
||||
}
|
||||
|
||||
func (ch suicideChange) revert(s *StateDB) {
|
||||
func (ch selfDestructChange) revert(s *StateDB) {
|
||||
obj := s.getStateObject(*ch.account)
|
||||
if obj != nil {
|
||||
obj.suicided = ch.prev
|
||||
obj.selfDestructed = ch.prev
|
||||
obj.setBalance(ch.prevbalance)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch suicideChange) dirtied() *common.Address {
|
||||
func (ch selfDestructChange) dirtied() *common.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
|
|
|
@ -27,4 +27,11 @@ var (
|
|||
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
|
||||
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
|
||||
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
|
||||
|
||||
slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil)
|
||||
slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil)
|
||||
slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil)
|
||||
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
|
||||
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
|
||||
slotDeletionSkip = metrics.NewRegisteredGauge("state/delete/storage/skip", nil)
|
||||
)
|
||||
|
|
|
@ -57,7 +57,6 @@ const (
|
|||
// Config includes all the configurations for pruning.
|
||||
type Config struct {
|
||||
Datadir string // The directory of the state database
|
||||
Cachedir string // The directory of state clean cache
|
||||
BloomSize uint64 // The Megabytes of memory allocated to bloom-filter
|
||||
}
|
||||
|
||||
|
@ -241,7 +240,7 @@ func (p *Pruner) Prune(root common.Hash) error {
|
|||
return err
|
||||
}
|
||||
if stateBloomRoot != (common.Hash{}) {
|
||||
return RecoverPruning(p.config.Datadir, p.db, p.config.Cachedir)
|
||||
return RecoverPruning(p.config.Datadir, p.db)
|
||||
}
|
||||
// If the target state root is not specified, use the HEAD-127 as the
|
||||
// target. The reason for picking it is:
|
||||
|
@ -299,12 +298,6 @@ func (p *Pruner) Prune(root common.Hash) error {
|
|||
log.Info("Selecting user-specified state as the pruning target", "root", root)
|
||||
}
|
||||
}
|
||||
// Before start the pruning, delete the clean trie cache first.
|
||||
// It's necessary otherwise in the next restart we will hit the
|
||||
// deleted state root in the "clean cache" so that the incomplete
|
||||
// state is picked for usage.
|
||||
deleteCleanTrieCache(p.config.Cachedir)
|
||||
|
||||
// All the state roots of the middle layer should be forcibly pruned,
|
||||
// otherwise the dangling state will be left.
|
||||
middleRoots := make(map[common.Hash]struct{})
|
||||
|
@ -342,7 +335,7 @@ func (p *Pruner) Prune(root common.Hash) error {
|
|||
// pruning can be resumed. What's more if the bloom filter is constructed, the
|
||||
// pruning **has to be resumed**. Otherwise a lot of dangling nodes may be left
|
||||
// in the disk.
|
||||
func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) error {
|
||||
func RecoverPruning(datadir string, db ethdb.Database) error {
|
||||
stateBloomPath, stateBloomRoot, err := findBloomFilter(datadir)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -378,12 +371,6 @@ func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) err
|
|||
}
|
||||
log.Info("Loaded state bloom filter", "path", stateBloomPath)
|
||||
|
||||
// Before start the pruning, delete the clean trie cache first.
|
||||
// It's necessary otherwise in the next restart we will hit the
|
||||
// deleted state root in the "clean cache" so that the incomplete
|
||||
// state is picked for usage.
|
||||
deleteCleanTrieCache(trieCachePath)
|
||||
|
||||
// All the state roots of the middle layers should be forcibly pruned,
|
||||
// otherwise the dangling state will be left.
|
||||
var (
|
||||
|
@ -420,7 +407,10 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accIter := t.NodeIterator(nil)
|
||||
accIter, err := t.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for accIter.Next(true) {
|
||||
hash := accIter.Hash()
|
||||
|
||||
|
@ -441,7 +431,10 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
storageIter := storageTrie.NodeIterator(nil)
|
||||
storageIter, err := storageTrie.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for storageIter.Next(true) {
|
||||
hash := storageIter.Hash()
|
||||
if hash != (common.Hash{}) {
|
||||
|
@ -491,23 +484,3 @@ func findBloomFilter(datadir string) (string, common.Hash, error) {
|
|||
}
|
||||
return stateBloomPath, stateBloomRoot, nil
|
||||
}
|
||||
|
||||
const warningLog = `
|
||||
|
||||
WARNING!
|
||||
|
||||
The clean trie cache is not found. Please delete it by yourself after the
|
||||
pruning. Remember don't start the Geth without deleting the clean trie cache
|
||||
otherwise the entire database may be damaged!
|
||||
|
||||
Check the command description "geth snapshot prune-state --help" for more details.
|
||||
`
|
||||
|
||||
func deleteCleanTrieCache(path string) {
|
||||
if !common.FileExist(path) {
|
||||
log.Warn(warningLog)
|
||||
return
|
||||
}
|
||||
os.RemoveAll(path)
|
||||
log.Info("Deleted trie clean cache", "path", path)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
@ -30,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
bloomfilter "github.com/holiman/bloomfilter/v2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -525,7 +525,7 @@ func (dl *diffLayer) AccountList() []common.Hash {
|
|||
dl.accountList = append(dl.accountList, hash)
|
||||
}
|
||||
}
|
||||
sort.Sort(hashes(dl.accountList))
|
||||
slices.SortFunc(dl.accountList, common.Hash.Less)
|
||||
dl.memory += uint64(len(dl.accountList) * common.HashLength)
|
||||
return dl.accountList
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool)
|
|||
for k := range storageMap {
|
||||
storageList = append(storageList, k)
|
||||
}
|
||||
sort.Sort(hashes(storageList))
|
||||
slices.SortFunc(storageList, common.Hash.Less)
|
||||
dl.storageList[accountHash] = storageList
|
||||
dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength)
|
||||
return storageList, destructed
|
||||
|
|
|
@ -256,7 +256,7 @@ func (dl *diskLayer) proveRange(ctx *generatorContext, trieId *trie.ID, prefix [
|
|||
if origin == nil {
|
||||
origin = common.Hash{}.Bytes()
|
||||
}
|
||||
if err := tr.Prove(origin, 0, proof); err != nil {
|
||||
if err := tr.Prove(origin, proof); err != nil {
|
||||
log.Debug("Failed to prove range", "kind", kind, "origin", origin, "err", err)
|
||||
return &proofResult{
|
||||
keys: keys,
|
||||
|
@ -267,7 +267,7 @@ func (dl *diskLayer) proveRange(ctx *generatorContext, trieId *trie.ID, prefix [
|
|||
}, nil
|
||||
}
|
||||
if last != nil {
|
||||
if err := tr.Prove(last, 0, proof); err != nil {
|
||||
if err := tr.Prove(last, proof); err != nil {
|
||||
log.Debug("Failed to prove range", "kind", kind, "last", last, "err", err)
|
||||
return &proofResult{
|
||||
keys: keys,
|
||||
|
@ -361,9 +361,12 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
|
|||
for i, key := range result.keys {
|
||||
snapTrie.Update(key, result.vals[i])
|
||||
}
|
||||
root, nodes := snapTrie.Commit(false)
|
||||
root, nodes, err := snapTrie.Commit(false)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if nodes != nil {
|
||||
tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
||||
tdb.Commit(root, false)
|
||||
}
|
||||
resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte {
|
||||
|
@ -382,8 +385,6 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
|
|||
}
|
||||
var (
|
||||
trieMore bool
|
||||
nodeIt = tr.NodeIterator(origin)
|
||||
iter = trie.NewIterator(nodeIt)
|
||||
kvkeys, kvvals = result.keys, result.vals
|
||||
|
||||
// counters
|
||||
|
@ -397,7 +398,12 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
|
|||
start = time.Now()
|
||||
internal time.Duration
|
||||
)
|
||||
nodeIt, err := tr.NodeIterator(origin)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
nodeIt.AddResolver(resolver)
|
||||
iter := trie.NewIterator(nodeIt)
|
||||
|
||||
for iter.Next() {
|
||||
if last != nil && bytes.Compare(iter.Key, last) > 0 {
|
||||
|
|
|
@ -191,7 +191,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st
|
|||
if !commit {
|
||||
return stTrie.Hash()
|
||||
}
|
||||
root, nodes := stTrie.Commit(false)
|
||||
root, nodes, _ := stTrie.Commit(false)
|
||||
if nodes != nil {
|
||||
t.nodes.Merge(nodes)
|
||||
}
|
||||
|
@ -199,11 +199,11 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st
|
|||
}
|
||||
|
||||
func (t *testHelper) Commit() common.Hash {
|
||||
root, nodes := t.accTrie.Commit(true)
|
||||
root, nodes, _ := t.accTrie.Commit(true)
|
||||
if nodes != nil {
|
||||
t.nodes.Merge(nodes)
|
||||
}
|
||||
t.triedb.Update(root, types.EmptyRootHash, t.nodes)
|
||||
t.triedb.Update(root, types.EmptyRootHash, 0, t.nodes, nil)
|
||||
t.triedb.Commit(root, false)
|
||||
return root
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// weightedIterator is a iterator with an assigned weight. It is used to prioritise
|
||||
|
@ -32,18 +33,10 @@ type weightedIterator struct {
|
|||
priority int
|
||||
}
|
||||
|
||||
// weightedIterators is a set of iterators implementing the sort.Interface.
|
||||
type weightedIterators []*weightedIterator
|
||||
|
||||
// Len implements sort.Interface, returning the number of active iterators.
|
||||
func (its weightedIterators) Len() int { return len(its) }
|
||||
|
||||
// Less implements sort.Interface, returning which of two iterators in the stack
|
||||
// is before the other.
|
||||
func (its weightedIterators) Less(i, j int) bool {
|
||||
func (it *weightedIterator) Less(other *weightedIterator) bool {
|
||||
// Order the iterators primarily by the account hashes
|
||||
hashI := its[i].it.Hash()
|
||||
hashJ := its[j].it.Hash()
|
||||
hashI := it.it.Hash()
|
||||
hashJ := other.it.Hash()
|
||||
|
||||
switch bytes.Compare(hashI[:], hashJ[:]) {
|
||||
case -1:
|
||||
|
@ -52,12 +45,7 @@ func (its weightedIterators) Less(i, j int) bool {
|
|||
return false
|
||||
}
|
||||
// Same account/storage-slot in multiple layers, split by priority
|
||||
return its[i].priority < its[j].priority
|
||||
}
|
||||
|
||||
// Swap implements sort.Interface, swapping two entries in the iterator stack.
|
||||
func (its weightedIterators) Swap(i, j int) {
|
||||
its[i], its[j] = its[j], its[i]
|
||||
return it.priority < other.priority
|
||||
}
|
||||
|
||||
// fastIterator is a more optimized multi-layer iterator which maintains a
|
||||
|
@ -69,7 +57,7 @@ type fastIterator struct {
|
|||
curAccount []byte
|
||||
curSlot []byte
|
||||
|
||||
iterators weightedIterators
|
||||
iterators []*weightedIterator
|
||||
initiated bool
|
||||
account bool
|
||||
fail error
|
||||
|
@ -167,7 +155,9 @@ func (fi *fastIterator) init() {
|
|||
}
|
||||
}
|
||||
// Re-sort the entire list
|
||||
sort.Sort(fi.iterators)
|
||||
slices.SortFunc(fi.iterators, func(a, b *weightedIterator) bool {
|
||||
return a.Less(b)
|
||||
})
|
||||
fi.initiated = false
|
||||
}
|
||||
|
||||
|
|
|
@ -79,29 +79,38 @@ const (
|
|||
// stateObject represents an Ethereum account which is being modified.
|
||||
//
|
||||
// The usage pattern is as follows:
|
||||
// First you need to obtain a state object.
|
||||
// Account values can be accessed and modified through the object.
|
||||
// Finally, call commitTrie to write the modified storage trie into a database.
|
||||
// - First you need to obtain a state object.
|
||||
// - Account values as well as storages can be accessed and modified through the object.
|
||||
// - Finally, call commit to return the changes of storage trie and update account data.
|
||||
type stateObject struct {
|
||||
address common.Address
|
||||
addrHash common.Hash // hash of ethereum address of the account
|
||||
data types.StateAccount
|
||||
db *StateDB
|
||||
address common.Address // address of ethereum account
|
||||
addrHash common.Hash // hash of ethereum address of the account
|
||||
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
||||
data types.StateAccount // Account data with all mutations applied in the scope of block
|
||||
|
||||
// Write caches.
|
||||
trie Trie // storage trie, which becomes non-nil on first access
|
||||
code Code // contract bytecode, which gets set when code is loaded
|
||||
|
||||
originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
|
||||
originStorage Storage // Storage cache of original entries to dedup rewrites
|
||||
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
|
||||
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
|
||||
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution, reset for every transaction
|
||||
|
||||
// Cache flags.
|
||||
// When an object is marked suicided it will be deleted from the trie
|
||||
// during the "update" phase of the state transition.
|
||||
dirtyCode bool // true if the code was updated
|
||||
suicided bool
|
||||
deleted bool
|
||||
|
||||
// Flag whether the account was marked as self-destructed. The self-destructed account
|
||||
// is still accessible in the scope of same transaction.
|
||||
selfDestructed bool
|
||||
|
||||
// Flag whether the account was marked as deleted. A self-destructed account
|
||||
// or an account that is considered as empty will be marked as deleted at
|
||||
// the end of transaction and no longer accessible anymore.
|
||||
deleted bool
|
||||
|
||||
// Flag whether the object was created in the current transaction
|
||||
created bool
|
||||
}
|
||||
|
||||
// empty returns whether the account is considered empty.
|
||||
|
@ -110,21 +119,17 @@ func (s *stateObject) empty() bool {
|
|||
}
|
||||
|
||||
// newObject creates a state object.
|
||||
func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject {
|
||||
if data.Balance == nil {
|
||||
data.Balance = new(big.Int)
|
||||
}
|
||||
if data.CodeHash == nil {
|
||||
data.CodeHash = types.EmptyCodeHash.Bytes()
|
||||
}
|
||||
if data.Root == (common.Hash{}) {
|
||||
data.Root = types.EmptyRootHash
|
||||
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
|
||||
origin := acct
|
||||
if acct == nil {
|
||||
acct = types.NewEmptyStateAccount()
|
||||
}
|
||||
return &stateObject{
|
||||
db: db,
|
||||
address: address,
|
||||
addrHash: crypto.Keccak256Hash(address[:]),
|
||||
data: data,
|
||||
origin: origin,
|
||||
data: *acct,
|
||||
originStorage: make(Storage),
|
||||
pendingStorage: make(Storage),
|
||||
dirtyStorage: make(Storage),
|
||||
|
@ -136,8 +141,8 @@ func (s *stateObject) EncodeRLP(w io.Writer) error {
|
|||
return rlp.Encode(w, &s.data)
|
||||
}
|
||||
|
||||
func (s *stateObject) markSuicided() {
|
||||
s.suicided = true
|
||||
func (s *stateObject) markSelfdestructed() {
|
||||
s.selfDestructed = true
|
||||
}
|
||||
|
||||
func (s *stateObject) touch() {
|
||||
|
@ -154,17 +159,15 @@ func (s *stateObject) touch() {
|
|||
// getTrie returns the associated storage trie. The trie will be opened
|
||||
// if it's not loaded previously. An error will be returned if trie can't
|
||||
// be loaded.
|
||||
func (s *stateObject) getTrie(db Database) (Trie, error) {
|
||||
func (s *stateObject) getTrie() (Trie, error) {
|
||||
if s.trie == nil {
|
||||
// Try fetching from prefetcher first
|
||||
// We don't prefetch empty tries
|
||||
if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
|
||||
// When the miner is creating the pending state, there is no
|
||||
// prefetcher
|
||||
// When the miner is creating the pending state, there is no prefetcher
|
||||
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
|
||||
}
|
||||
if s.trie == nil {
|
||||
tr, err := db.OpenStorageTrie(s.db.originalRoot, s.addrHash, s.data.Root)
|
||||
tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -175,18 +178,18 @@ func (s *stateObject) getTrie(db Database) (Trie, error) {
|
|||
}
|
||||
|
||||
// GetState retrieves a value from the account storage trie.
|
||||
func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
||||
// If we have a dirty value for this state entry, return it
|
||||
value, dirty := s.dirtyStorage[key]
|
||||
if dirty {
|
||||
return value
|
||||
}
|
||||
// Otherwise return the entry's original value
|
||||
return s.GetCommittedState(db, key)
|
||||
return s.GetCommittedState(key)
|
||||
}
|
||||
|
||||
// GetCommittedState retrieves a value from the committed account storage trie.
|
||||
func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
|
||||
func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||
// If we have a pending write or clean cached, return that
|
||||
if value, pending := s.pendingStorage[key]; pending {
|
||||
return value
|
||||
|
@ -226,7 +229,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
|||
// If the snapshot is unavailable or reading from it fails, load from the database.
|
||||
if s.db.snap == nil || err != nil {
|
||||
start := time.Now()
|
||||
tr, err := s.getTrie(db)
|
||||
tr, err := s.getTrie()
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
return common.Hash{}
|
||||
|
@ -246,9 +249,9 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
|||
}
|
||||
|
||||
// SetState updates a value in account storage.
|
||||
func (s *stateObject) SetState(db Database, key, value common.Hash) {
|
||||
func (s *stateObject) SetState(key, value common.Hash) {
|
||||
// If the new value is the same as old, don't set
|
||||
prev := s.GetState(db, key)
|
||||
prev := s.GetState(key)
|
||||
if prev == value {
|
||||
return
|
||||
}
|
||||
|
@ -289,7 +292,7 @@ func (s *stateObject) finalise(prefetch bool) {
|
|||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
// It will return nil if the trie has not been loaded and no changes have been
|
||||
// made. An error will be returned if the trie can't be loaded/updated correctly.
|
||||
func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
||||
func (s *stateObject) updateTrie() (Trie, error) {
|
||||
// Make sure all dirty slots are finalized into the pending storage area
|
||||
s.finalise(false) // Don't prefetch anymore, pull directly if need be
|
||||
if len(s.pendingStorage) == 0 {
|
||||
|
@ -302,9 +305,10 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
|||
// The snapshot storage map for the object
|
||||
var (
|
||||
storage map[common.Hash][]byte
|
||||
origin map[common.Hash][]byte
|
||||
hasher = s.db.hasher
|
||||
)
|
||||
tr, err := s.getTrie(db)
|
||||
tr, err := s.getTrie()
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
return nil, err
|
||||
|
@ -316,6 +320,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
|||
if value == s.originStorage[key] {
|
||||
continue
|
||||
}
|
||||
prev := s.originStorage[key]
|
||||
s.originStorage[key] = value
|
||||
|
||||
// rlp-encoded value to be used by the snapshot
|
||||
|
@ -336,17 +341,34 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
|||
}
|
||||
s.db.StorageUpdated += 1
|
||||
}
|
||||
// If state snapshotting is active, cache the data til commit
|
||||
if s.db.snap != nil {
|
||||
if storage == nil {
|
||||
// Retrieve the old storage map, if available, create a new one otherwise
|
||||
if storage = s.db.snapStorage[s.addrHash]; storage == nil {
|
||||
storage = make(map[common.Hash][]byte)
|
||||
s.db.snapStorage[s.addrHash] = storage
|
||||
}
|
||||
// Cache the mutated storage slots until commit
|
||||
if storage == nil {
|
||||
if storage = s.db.storages[s.addrHash]; storage == nil {
|
||||
storage = make(map[common.Hash][]byte)
|
||||
s.db.storages[s.addrHash] = storage
|
||||
}
|
||||
storage[crypto.HashData(hasher, key[:])] = snapshotVal // will be nil if it's deleted
|
||||
}
|
||||
khash := crypto.HashData(hasher, key[:])
|
||||
storage[khash] = snapshotVal // snapshotVal will be nil if it's deleted
|
||||
|
||||
// Cache the original value of mutated storage slots
|
||||
if origin == nil {
|
||||
if origin = s.db.storagesOrigin[s.addrHash]; origin == nil {
|
||||
origin = make(map[common.Hash][]byte)
|
||||
s.db.storagesOrigin[s.addrHash] = origin
|
||||
}
|
||||
}
|
||||
// Track the original value of slot only if it's mutated first time
|
||||
if _, ok := origin[khash]; !ok {
|
||||
if prev == (common.Hash{}) {
|
||||
origin[khash] = nil // nil if it was not present previously
|
||||
} else {
|
||||
// Encoding []byte cannot fail, ok to ignore the error.
|
||||
b, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(prev[:]))
|
||||
origin[khash] = b
|
||||
}
|
||||
}
|
||||
// Cache the items for preloading
|
||||
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
|
||||
}
|
||||
if s.db.prefetcher != nil {
|
||||
|
@ -360,8 +382,8 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
|||
|
||||
// UpdateRoot sets the trie root to the current root hash of. An error
|
||||
// will be returned if trie root hash is not computed correctly.
|
||||
func (s *stateObject) updateRoot(db Database) {
|
||||
tr, err := s.updateTrie(db)
|
||||
func (s *stateObject) updateRoot() {
|
||||
tr, err := s.updateTrie()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -376,23 +398,29 @@ func (s *stateObject) updateRoot(db Database) {
|
|||
s.data.Root = tr.Hash()
|
||||
}
|
||||
|
||||
// commitTrie submits the storage changes into the storage trie and re-computes
|
||||
// the root. Besides, all trie changes will be collected in a nodeset and returned.
|
||||
func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) {
|
||||
tr, err := s.updateTrie(db)
|
||||
// commit returns the changes made in storage trie and updates the account data.
|
||||
func (s *stateObject) commit() (*trienode.NodeSet, error) {
|
||||
tr, err := s.updateTrie()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If nothing changed, don't bother with committing anything
|
||||
if tr == nil {
|
||||
s.origin = s.data.Copy()
|
||||
return nil, nil
|
||||
}
|
||||
// Track the amount of time wasted on committing the storage trie
|
||||
if metrics.EnabledExpensive {
|
||||
defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now())
|
||||
}
|
||||
root, nodes := tr.Commit(false)
|
||||
root, nodes, err := tr.Commit(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.data.Root = root
|
||||
|
||||
// Update original account data after commit
|
||||
s.origin = s.data.Copy()
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
|
@ -435,18 +463,24 @@ func (s *stateObject) setBalance(amount *big.Int) {
|
|||
}
|
||||
|
||||
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||
stateObject := newObject(db, s.address, s.data)
|
||||
if s.trie != nil {
|
||||
stateObject.trie = db.db.CopyTrie(s.trie)
|
||||
obj := &stateObject{
|
||||
db: db,
|
||||
address: s.address,
|
||||
addrHash: s.addrHash,
|
||||
origin: s.origin,
|
||||
data: s.data,
|
||||
}
|
||||
stateObject.code = s.code
|
||||
stateObject.dirtyStorage = s.dirtyStorage.Copy()
|
||||
stateObject.originStorage = s.originStorage.Copy()
|
||||
stateObject.pendingStorage = s.pendingStorage.Copy()
|
||||
stateObject.suicided = s.suicided
|
||||
stateObject.dirtyCode = s.dirtyCode
|
||||
stateObject.deleted = s.deleted
|
||||
return stateObject
|
||||
if s.trie != nil {
|
||||
obj.trie = db.db.CopyTrie(s.trie)
|
||||
}
|
||||
obj.code = s.code
|
||||
obj.dirtyStorage = s.dirtyStorage.Copy()
|
||||
obj.originStorage = s.originStorage.Copy()
|
||||
obj.pendingStorage = s.pendingStorage.Copy()
|
||||
obj.selfDestructed = s.selfDestructed
|
||||
obj.dirtyCode = s.dirtyCode
|
||||
obj.deleted = s.deleted
|
||||
return obj
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -459,14 +493,14 @@ func (s *stateObject) Address() common.Address {
|
|||
}
|
||||
|
||||
// Code returns the contract code associated with this object, if any.
|
||||
func (s *stateObject) Code(db Database) []byte {
|
||||
func (s *stateObject) Code() []byte {
|
||||
if s.code != nil {
|
||||
return s.code
|
||||
}
|
||||
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
|
||||
return nil
|
||||
}
|
||||
code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash()))
|
||||
code, err := s.db.db.ContractCode(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
|
@ -477,14 +511,14 @@ func (s *stateObject) Code(db Database) []byte {
|
|||
// CodeSize returns the size of the contract code associated with this object,
|
||||
// or zero if none. This method is an almost mirror of Code, but uses a cache
|
||||
// inside the database to avoid loading codes seen recently.
|
||||
func (s *stateObject) CodeSize(db Database) int {
|
||||
func (s *stateObject) CodeSize() int {
|
||||
if s.code != nil {
|
||||
return len(s.code)
|
||||
}
|
||||
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
|
||||
return 0
|
||||
}
|
||||
size, err := db.ContractCodeSize(s.addrHash, common.BytesToHash(s.CodeHash()))
|
||||
size, err := s.db.db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
|
@ -492,7 +526,7 @@ func (s *stateObject) CodeSize(db Database) int {
|
|||
}
|
||||
|
||||
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||
prevcode := s.Code(s.db.db)
|
||||
prevcode := s.Code()
|
||||
s.db.journal.append(codeChange{
|
||||
account: &s.address,
|
||||
prevhash: s.CodeHash(),
|
||||
|
|
|
@ -30,21 +30,22 @@ import (
|
|||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type stateTest struct {
|
||||
type stateEnv struct {
|
||||
db ethdb.Database
|
||||
state *StateDB
|
||||
}
|
||||
|
||||
func newStateTest() *stateTest {
|
||||
func newStateEnv() *stateEnv {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
sdb, _ := New(types.EmptyRootHash, NewDatabase(db), nil)
|
||||
return &stateTest{db: db, state: sdb}
|
||||
return &stateEnv{db: db, state: sdb}
|
||||
}
|
||||
|
||||
func TestDump(t *testing.T) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
sdb, _ := New(types.EmptyRootHash, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil)
|
||||
s := &stateTest{db: db, state: sdb}
|
||||
tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
||||
sdb, _ := New(types.EmptyRootHash, tdb, nil)
|
||||
s := &stateEnv{db: db, state: sdb}
|
||||
|
||||
// generate a few entries
|
||||
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
||||
|
@ -57,9 +58,10 @@ func TestDump(t *testing.T) {
|
|||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
s.state.updateStateObject(obj2)
|
||||
s.state.Commit(false)
|
||||
root, _ := s.state.Commit(0, false)
|
||||
|
||||
// check that DumpToCollector contains the state objects that are in trie
|
||||
s.state, _ = New(root, tdb, nil)
|
||||
got := string(s.state.Dump(nil))
|
||||
want := `{
|
||||
"root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
|
||||
|
@ -95,8 +97,9 @@ func TestDump(t *testing.T) {
|
|||
|
||||
func TestIterativeDump(t *testing.T) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
sdb, _ := New(types.EmptyRootHash, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil)
|
||||
s := &stateTest{db: db, state: sdb}
|
||||
tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
||||
sdb, _ := New(types.EmptyRootHash, tdb, nil)
|
||||
s := &stateEnv{db: db, state: sdb}
|
||||
|
||||
// generate a few entries
|
||||
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
||||
|
@ -111,7 +114,8 @@ func TestIterativeDump(t *testing.T) {
|
|||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
s.state.updateStateObject(obj2)
|
||||
s.state.Commit(false)
|
||||
root, _ := s.state.Commit(0, false)
|
||||
s.state, _ = New(root, tdb, nil)
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
s.state.IterativeDump(nil, json.NewEncoder(b))
|
||||
|
@ -129,14 +133,14 @@ func TestIterativeDump(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNull(t *testing.T) {
|
||||
s := newStateTest()
|
||||
s := newStateEnv()
|
||||
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
|
||||
s.state.CreateAccount(address)
|
||||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||
var value common.Hash
|
||||
|
||||
s.state.SetState(address, common.Hash{}, value)
|
||||
s.state.Commit(false)
|
||||
s.state.Commit(0, false)
|
||||
|
||||
if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
|
||||
t.Errorf("expected empty current value, got %x", value)
|
||||
|
@ -151,7 +155,7 @@ func TestSnapshot(t *testing.T) {
|
|||
var storageaddr common.Hash
|
||||
data1 := common.BytesToHash([]byte{42})
|
||||
data2 := common.BytesToHash([]byte{43})
|
||||
s := newStateTest()
|
||||
s := newStateEnv()
|
||||
|
||||
// snapshot the genesis state
|
||||
genesis := s.state.Snapshot()
|
||||
|
@ -182,7 +186,7 @@ func TestSnapshot(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSnapshotEmpty(t *testing.T) {
|
||||
s := newStateTest()
|
||||
s := newStateEnv()
|
||||
s.state.RevertToSnapshot(s.state.Snapshot())
|
||||
}
|
||||
|
||||
|
@ -204,11 +208,11 @@ func TestSnapshot2(t *testing.T) {
|
|||
so0.SetBalance(big.NewInt(42), 0x0)
|
||||
so0.SetNonce(43)
|
||||
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
||||
so0.suicided = false
|
||||
so0.selfDestructed = false
|
||||
so0.deleted = false
|
||||
state.setStateObject(so0)
|
||||
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
||||
// and one with deleted == true
|
||||
|
@ -216,7 +220,7 @@ func TestSnapshot2(t *testing.T) {
|
|||
so1.SetBalance(big.NewInt(52), 0x0)
|
||||
so1.SetNonce(53)
|
||||
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
|
||||
so1.suicided = true
|
||||
so1.selfDestructed = true
|
||||
so1.deleted = true
|
||||
state.setStateObject(so1)
|
||||
|
||||
|
@ -230,8 +234,8 @@ func TestSnapshot2(t *testing.T) {
|
|||
|
||||
so0Restored := state.getStateObject(stateobjaddr0)
|
||||
// Update lazily-loaded values before comparing.
|
||||
so0Restored.GetState(state.db, storageaddr)
|
||||
so0Restored.Code(state.db)
|
||||
so0Restored.GetState(storageaddr)
|
||||
so0Restored.Code()
|
||||
// non-deleted is equal (restored)
|
||||
compareStateObjects(so0Restored, so0, t)
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||
)
|
||||
|
||||
type revision struct {
|
||||
|
@ -69,29 +70,40 @@ type StateLogger interface {
|
|||
// StateDB structs within the ethereum protocol are used to store anything
|
||||
// within the merkle trie. StateDBs take care of caching and storing
|
||||
// nested states. It's the general query interface to retrieve:
|
||||
//
|
||||
// * Contracts
|
||||
// * Accounts
|
||||
//
|
||||
// Once the state is committed, tries cached in stateDB (including account
|
||||
// trie, storage tries) will no longer be functional. A new state instance
|
||||
// must be created with new root and updated database for accessing post-
|
||||
// commit states.
|
||||
type StateDB struct {
|
||||
db Database
|
||||
prefetcher *triePrefetcher
|
||||
trie Trie
|
||||
hasher crypto.KeccakState
|
||||
logger StateLogger
|
||||
snaps *snapshot.Tree // Nil if snapshot is not available
|
||||
snap snapshot.Snapshot // Nil if snapshot is not available
|
||||
|
||||
// originalRoot is the pre-state root, before any changes were made.
|
||||
// It will be updated when the Commit is called.
|
||||
originalRoot common.Hash
|
||||
|
||||
snaps *snapshot.Tree
|
||||
snap snapshot.Snapshot
|
||||
snapAccounts map[common.Hash][]byte
|
||||
snapStorage map[common.Hash]map[common.Hash][]byte
|
||||
// These maps hold the state changes (including the corresponding
|
||||
// original value) that occurred in this **block**.
|
||||
accounts map[common.Hash][]byte // The mutated accounts in 'slim RLP' encoding
|
||||
storages map[common.Hash]map[common.Hash][]byte // The mutated slots in prefix-zero trimmed rlp format
|
||||
accountsOrigin map[common.Hash][]byte // The original value of mutated accounts in 'slim RLP' encoding
|
||||
storagesOrigin map[common.Hash]map[common.Hash][]byte // The original value of mutated slots in prefix-zero trimmed rlp format
|
||||
|
||||
// This map holds 'live' objects, which will get modified while processing a state transition.
|
||||
// This map holds 'live' objects, which will get modified while processing
|
||||
// a state transition.
|
||||
stateObjects map[common.Address]*stateObject
|
||||
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
|
||||
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
||||
stateObjectsDestruct map[common.Address]struct{} // State objects destructed in the block
|
||||
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
|
||||
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
||||
stateObjectsDestruct map[common.Address]*types.StateAccount // State objects destructed in the block along with its previous value
|
||||
|
||||
// DB error.
|
||||
// State objects are used by the consensus core and VM which are
|
||||
|
@ -105,11 +117,13 @@ type StateDB struct {
|
|||
// The refund counter, also used by state transitioning.
|
||||
refund uint64
|
||||
|
||||
// The tx context and all occurred logs in the scope of transaction.
|
||||
thash common.Hash
|
||||
txIndex int
|
||||
logs map[common.Hash][]*types.Log
|
||||
logSize uint
|
||||
|
||||
// Preimages occurred seen by VM in the scope of block.
|
||||
preimages map[common.Hash][]byte
|
||||
|
||||
// Per-transaction access list
|
||||
|
@ -155,10 +169,14 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
|||
trie: tr,
|
||||
originalRoot: root,
|
||||
snaps: snaps,
|
||||
accounts: make(map[common.Hash][]byte),
|
||||
storages: make(map[common.Hash]map[common.Hash][]byte),
|
||||
accountsOrigin: make(map[common.Hash][]byte),
|
||||
storagesOrigin: make(map[common.Hash]map[common.Hash][]byte),
|
||||
stateObjects: make(map[common.Address]*stateObject),
|
||||
stateObjectsPending: make(map[common.Address]struct{}),
|
||||
stateObjectsDirty: make(map[common.Address]struct{}),
|
||||
stateObjectsDestruct: make(map[common.Address]struct{}),
|
||||
stateObjectsDestruct: make(map[common.Address]*types.StateAccount),
|
||||
logs: make(map[common.Hash][]*types.Log),
|
||||
preimages: make(map[common.Hash][]byte),
|
||||
journal: newJournal(),
|
||||
|
@ -167,10 +185,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
|||
hasher: crypto.NewKeccakState(),
|
||||
}
|
||||
if sdb.snaps != nil {
|
||||
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
|
||||
sdb.snapAccounts = make(map[common.Hash][]byte)
|
||||
sdb.snapStorage = make(map[common.Hash]map[common.Hash][]byte)
|
||||
}
|
||||
sdb.snap = sdb.snaps.Snapshot(root)
|
||||
}
|
||||
return sdb, nil
|
||||
}
|
||||
|
@ -278,7 +293,7 @@ func (s *StateDB) SubRefund(gas uint64) {
|
|||
}
|
||||
|
||||
// Exist reports whether the given account address exists in the state.
|
||||
// Notably this also returns true for suicided accounts.
|
||||
// Notably this also returns true for self-destructed accounts.
|
||||
func (s *StateDB) Exist(addr common.Address) bool {
|
||||
return s.getStateObject(addr) != nil
|
||||
}
|
||||
|
@ -316,7 +331,7 @@ func (s *StateDB) TxIndex() int {
|
|||
func (s *StateDB) GetCode(addr common.Address) []byte {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Code(s.db)
|
||||
return stateObject.Code()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -324,7 +339,7 @@ func (s *StateDB) GetCode(addr common.Address) []byte {
|
|||
func (s *StateDB) GetCodeSize(addr common.Address) int {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.CodeSize(s.db)
|
||||
return stateObject.CodeSize()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -341,7 +356,7 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
|||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.GetState(s.db, hash)
|
||||
return stateObject.GetState(hash)
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
|
@ -354,7 +369,7 @@ func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) {
|
|||
// GetProofByHash returns the Merkle proof for a given account.
|
||||
func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) {
|
||||
var proof proofList
|
||||
err := s.trie.Prove(addrHash[:], 0, &proof)
|
||||
err := s.trie.Prove(addrHash[:], &proof)
|
||||
return proof, err
|
||||
}
|
||||
|
||||
|
@ -368,7 +383,7 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
|
|||
return nil, errors.New("storage trie for requested address does not exist")
|
||||
}
|
||||
var proof proofList
|
||||
err = trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
|
||||
err = trie.Prove(crypto.Keccak256(key.Bytes()), &proof)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -379,7 +394,7 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
|
|||
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.GetCommittedState(s.db, hash)
|
||||
return stateObject.GetCommittedState(hash)
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
|
@ -398,16 +413,16 @@ func (s *StateDB) StorageTrie(addr common.Address) (Trie, error) {
|
|||
return nil, nil
|
||||
}
|
||||
cpy := stateObject.deepCopy(s)
|
||||
if _, err := cpy.updateTrie(s.db); err != nil {
|
||||
if _, err := cpy.updateTrie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cpy.getTrie(s.db)
|
||||
return cpy.getTrie()
|
||||
}
|
||||
|
||||
func (s *StateDB) HasSuicided(addr common.Address) bool {
|
||||
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.suicided
|
||||
return stateObject.selfDestructed
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -456,44 +471,59 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) {
|
|||
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
||||
stateObject := s.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetState(s.db, key, value)
|
||||
stateObject.SetState(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// SetStorage replaces the entire storage for the specified account with given
|
||||
// storage. This function should only be used for debugging.
|
||||
// storage. This function should only be used for debugging and the mutations
|
||||
// must be discarded afterwards.
|
||||
func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
|
||||
// SetStorage needs to wipe existing storage. We achieve this by pretending
|
||||
// that the account self-destructed earlier in this block, by flagging
|
||||
// it in stateObjectsDestruct. The effect of doing so is that storage lookups
|
||||
// will not hit disk, since it is assumed that the disk-data is belonging
|
||||
// to a previous incarnation of the object.
|
||||
s.stateObjectsDestruct[addr] = struct{}{}
|
||||
//
|
||||
// TODO(rjl493456442) this function should only be supported by 'unwritable'
|
||||
// state and all mutations made should all be discarded afterwards.
|
||||
if _, ok := s.stateObjectsDestruct[addr]; !ok {
|
||||
s.stateObjectsDestruct[addr] = nil
|
||||
}
|
||||
stateObject := s.GetOrNewStateObject(addr)
|
||||
for k, v := range storage {
|
||||
stateObject.SetState(s.db, k, v)
|
||||
stateObject.SetState(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Suicide marks the given account as suicided.
|
||||
// SelfDestruct marks the given account as selfdestructed.
|
||||
// This clears the account balance.
|
||||
//
|
||||
// The account's state object is still available until the state is committed,
|
||||
// getStateObject will return a non-nil account after Suicide.
|
||||
func (s *StateDB) Suicide(addr common.Address) bool {
|
||||
// getStateObject will return a non-nil account after SelfDestruct.
|
||||
func (s *StateDB) SelfDestruct(addr common.Address) {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject == nil {
|
||||
return false
|
||||
return
|
||||
}
|
||||
s.journal.append(suicideChange{
|
||||
s.journal.append(selfDestructChange{
|
||||
account: &addr,
|
||||
prev: stateObject.suicided,
|
||||
prev: stateObject.selfDestructed,
|
||||
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
||||
})
|
||||
stateObject.markSuicided()
|
||||
stateObject.markSelfdestructed()
|
||||
stateObject.data.Balance = new(big.Int)
|
||||
}
|
||||
|
||||
return true
|
||||
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if stateObject.created {
|
||||
s.SelfDestruct(addr)
|
||||
}
|
||||
}
|
||||
|
||||
// SetTransientState sets transient storage for a given account. It
|
||||
|
@ -538,13 +568,21 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
|||
if err := s.trie.UpdateAccount(addr, &obj.data); err != nil {
|
||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
||||
}
|
||||
// Cache the data until commit. Note, this update mechanism is not symmetric
|
||||
// to the deletion, because whereas it is enough to track account updates
|
||||
// at commit time, deletions need tracking at transaction boundary level to
|
||||
// ensure we capture state clearing.
|
||||
s.accounts[obj.addrHash] = types.SlimAccountRLP(obj.data)
|
||||
|
||||
// If state snapshotting is active, cache the data til commit. Note, this
|
||||
// update mechanism is not symmetric to the deletion, because whereas it is
|
||||
// enough to track account updates at commit time, deletions need tracking
|
||||
// at transaction boundary level to ensure we capture state clearing.
|
||||
if s.snap != nil {
|
||||
s.snapAccounts[obj.addrHash] = types.SlimAccountRLP(obj.data)
|
||||
// Track the original value of mutated account, nil means it was not present.
|
||||
// Skip if it has been tracked (because updateStateObject may be called
|
||||
// multiple times in a block).
|
||||
if _, ok := s.accountsOrigin[obj.addrHash]; !ok {
|
||||
if obj.origin == nil {
|
||||
s.accountsOrigin[obj.addrHash] = nil
|
||||
} else {
|
||||
s.accountsOrigin[obj.addrHash] = types.SlimAccountRLP(*obj.origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,7 +661,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
|
|||
}
|
||||
}
|
||||
// Insert into the live set
|
||||
obj := newObject(s, addr, *data)
|
||||
obj := newObject(s, addr, data)
|
||||
s.setStateObject(obj)
|
||||
return obj
|
||||
}
|
||||
|
@ -645,7 +683,7 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
|
|||
// the given address, it is overwritten and returned as the second return value.
|
||||
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
||||
prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
|
||||
newobj = newObject(s, addr, types.StateAccount{})
|
||||
newobj = newObject(s, addr, nil)
|
||||
if prev == nil {
|
||||
s.journal.append(createObjectChange{account: &addr})
|
||||
// TODO: add isPrecompile check
|
||||
|
@ -655,34 +693,34 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
|
|||
} else {
|
||||
// The original account should be marked as destructed and all cached
|
||||
// account and storage data should be cleared as well. Note, it must
|
||||
// be done here, otherwise the destruction event of original one will
|
||||
// be lost.
|
||||
// be done here, otherwise the destruction event of "original account"
|
||||
// will be lost.
|
||||
_, prevdestruct := s.stateObjectsDestruct[prev.address]
|
||||
if !prevdestruct {
|
||||
s.stateObjectsDestruct[prev.address] = struct{}{}
|
||||
s.stateObjectsDestruct[prev.address] = prev.origin
|
||||
}
|
||||
var (
|
||||
account []byte
|
||||
storage map[common.Hash][]byte
|
||||
)
|
||||
// There may be some cached account/storage data already since IntermediateRoot
|
||||
// will be called for each transaction before byzantium fork which will always
|
||||
// cache the latest account/storage data.
|
||||
if s.snap != nil {
|
||||
account = s.snapAccounts[prev.addrHash]
|
||||
storage = s.snapStorage[prev.addrHash]
|
||||
delete(s.snapAccounts, prev.addrHash)
|
||||
delete(s.snapStorage, prev.addrHash)
|
||||
}
|
||||
prevAccount, ok := s.accountsOrigin[prev.addrHash]
|
||||
s.journal.append(resetObjectChange{
|
||||
account: &addr,
|
||||
prev: prev,
|
||||
prevdestruct: prevdestruct,
|
||||
prevAccount: account,
|
||||
prevStorage: storage,
|
||||
account: &addr,
|
||||
prev: prev,
|
||||
prevdestruct: prevdestruct,
|
||||
prevAccount: s.accounts[prev.addrHash],
|
||||
prevStorage: s.storages[prev.addrHash],
|
||||
prevAccountOriginExist: ok,
|
||||
prevAccountOrigin: prevAccount,
|
||||
prevStorageOrigin: s.storagesOrigin[prev.addrHash],
|
||||
})
|
||||
delete(s.accounts, prev.addrHash)
|
||||
delete(s.storages, prev.addrHash)
|
||||
delete(s.accountsOrigin, prev.addrHash)
|
||||
delete(s.storagesOrigin, prev.addrHash)
|
||||
}
|
||||
|
||||
newobj.created = true
|
||||
|
||||
s.setStateObject(newobj)
|
||||
if prev != nil && !prev.deleted {
|
||||
return newobj, prev
|
||||
|
@ -707,19 +745,23 @@ func (s *StateDB) CreateAccount(addr common.Address) {
|
|||
}
|
||||
}
|
||||
|
||||
func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||
so := db.getStateObject(addr)
|
||||
func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||
so := s.getStateObject(addr)
|
||||
if so == nil {
|
||||
return nil
|
||||
}
|
||||
tr, err := so.getTrie(db.db)
|
||||
tr, err := so.getTrie()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it := trie.NewIterator(tr.NodeIterator(nil))
|
||||
trieIt, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it := trie.NewIterator(trieIt)
|
||||
|
||||
for it.Next() {
|
||||
key := common.BytesToHash(db.trie.GetKey(it.Key))
|
||||
key := common.BytesToHash(s.trie.GetKey(it.Key))
|
||||
if value, dirty := so.dirtyStorage[key]; dirty {
|
||||
if !cb(key, value) {
|
||||
return nil
|
||||
|
@ -748,16 +790,27 @@ func (s *StateDB) Copy() *StateDB {
|
|||
db: s.db,
|
||||
trie: s.db.CopyTrie(s.trie),
|
||||
originalRoot: s.originalRoot,
|
||||
accounts: make(map[common.Hash][]byte),
|
||||
storages: make(map[common.Hash]map[common.Hash][]byte),
|
||||
accountsOrigin: make(map[common.Hash][]byte),
|
||||
storagesOrigin: make(map[common.Hash]map[common.Hash][]byte),
|
||||
stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
|
||||
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
|
||||
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
|
||||
stateObjectsDestruct: make(map[common.Address]struct{}, len(s.stateObjectsDestruct)),
|
||||
stateObjectsDestruct: make(map[common.Address]*types.StateAccount, len(s.stateObjectsDestruct)),
|
||||
refund: s.refund,
|
||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||
logSize: s.logSize,
|
||||
preimages: make(map[common.Hash][]byte, len(s.preimages)),
|
||||
journal: newJournal(),
|
||||
hasher: crypto.NewKeccakState(),
|
||||
|
||||
// In order for the block producer to be able to use and make additions
|
||||
// to the snapshot tree, we need to copy that as well. Otherwise, any
|
||||
// block mined by ourselves will cause gaps in the tree, and force the
|
||||
// miner to operate trie-backed only.
|
||||
snaps: s.snaps,
|
||||
snap: s.snap,
|
||||
}
|
||||
// Copy the dirty states, logs, and preimages
|
||||
for addr := range s.journal.dirties {
|
||||
|
@ -791,10 +844,18 @@ func (s *StateDB) Copy() *StateDB {
|
|||
}
|
||||
state.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
// Deep copy the destruction flag.
|
||||
for addr := range s.stateObjectsDestruct {
|
||||
state.stateObjectsDestruct[addr] = struct{}{}
|
||||
// Deep copy the destruction markers.
|
||||
for addr, value := range s.stateObjectsDestruct {
|
||||
state.stateObjectsDestruct[addr] = value
|
||||
}
|
||||
// Deep copy the state changes made in the scope of block
|
||||
// along with their original values.
|
||||
state.accounts = copyAccounts(s.accounts)
|
||||
state.storages = copyStorages(s.storages)
|
||||
state.accountsOrigin = copyAccounts(state.accountsOrigin)
|
||||
state.storagesOrigin = copyStorages(state.storagesOrigin)
|
||||
|
||||
// Deep copy the logs occurred in the scope of block
|
||||
for hash, logs := range s.logs {
|
||||
cpy := make([]*types.Log, len(logs))
|
||||
for i, l := range logs {
|
||||
|
@ -803,6 +864,7 @@ func (s *StateDB) Copy() *StateDB {
|
|||
}
|
||||
state.logs[hash] = cpy
|
||||
}
|
||||
// Deep copy the preimages occurred in the scope of block
|
||||
for hash, preimage := range s.preimages {
|
||||
state.preimages[hash] = preimage
|
||||
}
|
||||
|
@ -821,28 +883,6 @@ func (s *StateDB) Copy() *StateDB {
|
|||
if s.prefetcher != nil {
|
||||
state.prefetcher = s.prefetcher.copy()
|
||||
}
|
||||
if s.snaps != nil {
|
||||
// In order for the miner to be able to use and make additions
|
||||
// to the snapshot tree, we need to copy that as well.
|
||||
// Otherwise, any block mined by ourselves will cause gaps in the tree,
|
||||
// and force the miner to operate trie-backed only
|
||||
state.snaps = s.snaps
|
||||
state.snap = s.snap
|
||||
|
||||
// deep copy needed
|
||||
state.snapAccounts = make(map[common.Hash][]byte, len(s.snapAccounts))
|
||||
for k, v := range s.snapAccounts {
|
||||
state.snapAccounts[k] = v
|
||||
}
|
||||
state.snapStorage = make(map[common.Hash]map[common.Hash][]byte, len(s.snapStorage))
|
||||
for k, v := range s.snapStorage {
|
||||
temp := make(map[common.Hash][]byte, len(v))
|
||||
for kk, vv := range v {
|
||||
temp[kk] = vv
|
||||
}
|
||||
state.snapStorage[k] = temp
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
|
@ -891,24 +931,26 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
|||
// Thus, we can safely ignore it here
|
||||
continue
|
||||
}
|
||||
if obj.suicided || (deleteEmptyObjects && obj.empty()) {
|
||||
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
||||
obj.deleted = true
|
||||
|
||||
// We need to maintain account deletions explicitly (will remain
|
||||
// set indefinitely).
|
||||
s.stateObjectsDestruct[obj.address] = struct{}{}
|
||||
|
||||
// If state snapshotting is active, also mark the destruction there.
|
||||
// set indefinitely). Note only the first occurred self-destruct
|
||||
// event is tracked.
|
||||
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||
s.stateObjectsDestruct[obj.address] = obj.origin
|
||||
}
|
||||
// Note, we can't do this only at the end of a block because multiple
|
||||
// transactions within the same block might self destruct and then
|
||||
// resurrect an account; but the snapshotter needs both events.
|
||||
if s.snap != nil {
|
||||
delete(s.snapAccounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect)
|
||||
delete(s.snapStorage, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect)
|
||||
}
|
||||
delete(s.accounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect)
|
||||
delete(s.storages, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect)
|
||||
delete(s.accountsOrigin, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect)
|
||||
delete(s.storagesOrigin, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect)
|
||||
} else {
|
||||
obj.finalise(true) // Prefetch slots in the background
|
||||
}
|
||||
obj.created = false
|
||||
s.stateObjectsPending[addr] = struct{}{}
|
||||
s.stateObjectsDirty[addr] = struct{}{}
|
||||
|
||||
|
@ -952,7 +994,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
// to pull useful data from disk.
|
||||
for addr := range s.stateObjectsPending {
|
||||
if obj := s.stateObjects[addr]; !obj.deleted {
|
||||
obj.updateRoot(s.db)
|
||||
obj.updateRoot()
|
||||
}
|
||||
}
|
||||
// Now we're about to start to write changes to the trie. The trie is so far
|
||||
|
@ -1003,8 +1045,146 @@ func (s *StateDB) clearJournalAndRefund() {
|
|||
s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries
|
||||
}
|
||||
|
||||
// deleteStorage iterates the storage trie belongs to the account and mark all
|
||||
// slots inside as deleted.
|
||||
func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (bool, map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||
start := time.Now()
|
||||
tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root)
|
||||
if err != nil {
|
||||
return false, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
||||
}
|
||||
it, err := tr.NodeIterator(nil)
|
||||
if err != nil {
|
||||
return false, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err)
|
||||
}
|
||||
var (
|
||||
set = trienode.NewNodeSet(addrHash)
|
||||
slots = make(map[common.Hash][]byte)
|
||||
stateSize common.StorageSize
|
||||
nodeSize common.StorageSize
|
||||
)
|
||||
for it.Next(true) {
|
||||
// arbitrary stateSize limit, make it configurable
|
||||
if stateSize+nodeSize > 512*1024*1024 {
|
||||
log.Info("Skip large storage deletion", "address", addr.Hex(), "states", stateSize, "nodes", nodeSize)
|
||||
if metrics.EnabledExpensive {
|
||||
slotDeletionSkip.Inc(1)
|
||||
}
|
||||
return true, nil, nil, nil
|
||||
}
|
||||
if it.Leaf() {
|
||||
slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob())
|
||||
stateSize += common.StorageSize(common.HashLength + len(it.LeafBlob()))
|
||||
continue
|
||||
}
|
||||
if it.Hash() == (common.Hash{}) {
|
||||
continue
|
||||
}
|
||||
nodeSize += common.StorageSize(len(it.Path()) + len(it.NodeBlob()))
|
||||
set.AddNode(it.Path(), trienode.NewWithPrev(common.Hash{}, nil, it.NodeBlob()))
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
if metrics.EnabledExpensive {
|
||||
if int64(len(slots)) > slotDeletionMaxCount.Value() {
|
||||
slotDeletionMaxCount.Update(int64(len(slots)))
|
||||
}
|
||||
if int64(stateSize+nodeSize) > slotDeletionMaxSize.Value() {
|
||||
slotDeletionMaxSize.Update(int64(stateSize + nodeSize))
|
||||
}
|
||||
slotDeletionTimer.UpdateSince(start)
|
||||
slotDeletionCount.Mark(int64(len(slots)))
|
||||
slotDeletionSize.Mark(int64(stateSize + nodeSize))
|
||||
}
|
||||
return false, slots, set, nil
|
||||
}
|
||||
|
||||
// handleDestruction processes all destruction markers and deletes the account
|
||||
// and associated storage slots if necessary. There are four possible situations
|
||||
// here:
|
||||
//
|
||||
// - the account was not existent and be marked as destructed
|
||||
//
|
||||
// - the account was not existent and be marked as destructed,
|
||||
// however, it's resurrected later in the same block.
|
||||
//
|
||||
// - the account was existent and be marked as destructed
|
||||
//
|
||||
// - the account was existent and be marked as destructed,
|
||||
// however it's resurrected later in the same block.
|
||||
//
|
||||
// In case (a), nothing needs be deleted, nil to nil transition can be ignored.
|
||||
//
|
||||
// In case (b), nothing needs be deleted, nil is used as the original value for
|
||||
// newly created account and storages
|
||||
//
|
||||
// In case (c), **original** account along with its storages should be deleted,
|
||||
// with their values be tracked as original value.
|
||||
//
|
||||
// In case (d), **original** account along with its storages should be deleted,
|
||||
// with their values be tracked as original value.
|
||||
func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.Hash]struct{}, error) {
|
||||
incomplete := make(map[common.Hash]struct{})
|
||||
for addr, prev := range s.stateObjectsDestruct {
|
||||
// The original account was non-existing, and it's marked as destructed
|
||||
// in the scope of block. It can be case (a) or (b).
|
||||
// - for (a), skip it without doing anything.
|
||||
// - for (b), track account's original value as nil. It may overwrite
|
||||
// the data cached in s.accountsOrigin set by 'updateStateObject'.
|
||||
addrHash := crypto.Keccak256Hash(addr[:])
|
||||
if prev == nil {
|
||||
if _, ok := s.accounts[addrHash]; ok {
|
||||
s.accountsOrigin[addrHash] = nil // case (b)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// It can overwrite the data in s.accountsOrigin set by 'updateStateObject'.
|
||||
s.accountsOrigin[addrHash] = types.SlimAccountRLP(*prev) // case (c) or (d)
|
||||
|
||||
// Short circuit if the storage was empty.
|
||||
if prev.Root == types.EmptyRootHash {
|
||||
continue
|
||||
}
|
||||
// Remove storage slots belong to the account.
|
||||
aborted, slots, set, err := s.deleteStorage(addr, addrHash, prev.Root)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to delete storage, err: %w", err)
|
||||
}
|
||||
// The storage is too huge to handle, skip it but mark as incomplete.
|
||||
// For case (d), the account is resurrected might with a few slots
|
||||
// created. In this case, wipe the entire storage state diff because
|
||||
// of aborted deletion.
|
||||
if aborted {
|
||||
incomplete[addrHash] = struct{}{}
|
||||
delete(s.storagesOrigin, addrHash)
|
||||
continue
|
||||
}
|
||||
if s.storagesOrigin[addrHash] == nil {
|
||||
s.storagesOrigin[addrHash] = slots
|
||||
} else {
|
||||
// It can overwrite the data in s.storagesOrigin[addrHash] set by
|
||||
// 'object.updateTrie'.
|
||||
for key, val := range slots {
|
||||
s.storagesOrigin[addrHash][key] = val
|
||||
}
|
||||
}
|
||||
if err := nodes.Merge(set); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return incomplete, nil
|
||||
}
|
||||
|
||||
// Commit writes the state to the underlying in-memory trie database.
|
||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
// Once the state is committed, tries cached in stateDB (including account
|
||||
// trie, storage tries) will no longer be functional. A new state instance
|
||||
// must be created with new root and updated database for accessing post-
|
||||
// commit states.
|
||||
//
|
||||
// The associated block number of the state transition is also provided
|
||||
// for more chain context.
|
||||
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, error) {
|
||||
// Short circuit in case any database failure occurred earlier.
|
||||
if s.dbErr != nil {
|
||||
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
||||
|
@ -1021,37 +1201,39 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||
nodes = trienode.NewMergedNodeSet()
|
||||
codeWriter = s.db.DiskDB().NewBatch()
|
||||
)
|
||||
// Handle all state deletions first
|
||||
incomplete, err := s.handleDestruction(nodes)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Handle all state updates afterwards
|
||||
for addr := range s.stateObjectsDirty {
|
||||
if obj := s.stateObjects[addr]; !obj.deleted {
|
||||
// Write any contract code associated with the state object
|
||||
if obj.code != nil && obj.dirtyCode {
|
||||
rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code)
|
||||
obj.dirtyCode = false
|
||||
}
|
||||
// Write any storage changes in the state object to its storage trie
|
||||
set, err := obj.commitTrie(s.db)
|
||||
if err != nil {
|
||||
obj := s.stateObjects[addr]
|
||||
if obj.deleted {
|
||||
continue
|
||||
}
|
||||
// Write any contract code associated with the state object
|
||||
if obj.code != nil && obj.dirtyCode {
|
||||
rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code)
|
||||
s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code)
|
||||
obj.dirtyCode = false
|
||||
}
|
||||
// Write any storage changes in the state object to its storage trie
|
||||
set, err := obj.commit()
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Merge the dirty nodes of storage trie into global set. It is possible
|
||||
// that the account was destructed and then resurrected in the same block.
|
||||
// In this case, the node set is shared by both accounts.
|
||||
if set != nil {
|
||||
if err := nodes.Merge(set); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Merge the dirty nodes of storage trie into global set.
|
||||
if set != nil {
|
||||
if err := nodes.Merge(set); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
updates, deleted := set.Size()
|
||||
storageTrieNodesUpdated += updates
|
||||
storageTrieNodesDeleted += deleted
|
||||
}
|
||||
updates, deleted := set.Size()
|
||||
storageTrieNodesUpdated += updates
|
||||
storageTrieNodesDeleted += deleted
|
||||
}
|
||||
// If the contract is destructed, the storage is still left in the
|
||||
// database as dangling data. Theoretically it's should be wiped from
|
||||
// database as well, but in hash-based-scheme it's extremely hard to
|
||||
// determine that if the trie nodes are also referenced by other storage,
|
||||
// and in path-based-scheme some technical challenges are still unsolved.
|
||||
// Although it won't affect the correctness but please fix it TODO(rjl493456442).
|
||||
}
|
||||
if len(s.stateObjectsDirty) > 0 {
|
||||
s.stateObjectsDirty = make(map[common.Address]struct{})
|
||||
}
|
||||
if codeWriter.ValueSize() > 0 {
|
||||
if err := codeWriter.Write(); err != nil {
|
||||
|
@ -1063,7 +1245,10 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||
if metrics.EnabledExpensive {
|
||||
start = time.Now()
|
||||
}
|
||||
root, set := s.trie.Commit(true)
|
||||
root, set, err := s.trie.Commit(true)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Merge the dirty nodes of account trie into global set
|
||||
if set != nil {
|
||||
if err := nodes.Merge(set); err != nil {
|
||||
|
@ -1090,7 +1275,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||
start := time.Now()
|
||||
// Only update if there's a state transition (skip empty Clique blocks)
|
||||
if parent := s.snap.Root(); parent != root {
|
||||
if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.snapAccounts, s.snapStorage); err != nil {
|
||||
if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
|
@ -1104,10 +1289,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||
if metrics.EnabledExpensive {
|
||||
s.SnapshotCommits += time.Since(start)
|
||||
}
|
||||
s.snap, s.snapAccounts, s.snapStorage = nil, nil, nil
|
||||
}
|
||||
if len(s.stateObjectsDestruct) > 0 {
|
||||
s.stateObjectsDestruct = make(map[common.Address]struct{})
|
||||
s.snap = nil
|
||||
}
|
||||
if root == (common.Hash{}) {
|
||||
root = types.EmptyRootHash
|
||||
|
@ -1118,7 +1300,12 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||
}
|
||||
if root != origin {
|
||||
start := time.Now()
|
||||
if err := s.db.TrieDB().Update(root, origin, nodes); err != nil {
|
||||
set := &triestate.Set{
|
||||
Accounts: s.accountsOrigin,
|
||||
Storages: s.storagesOrigin,
|
||||
Incomplete: incomplete,
|
||||
}
|
||||
if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
s.originalRoot = root
|
||||
|
@ -1126,6 +1313,13 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||
s.TrieDBCommits += time.Since(start)
|
||||
}
|
||||
}
|
||||
// Clear all internal flags at the end of commit operation.
|
||||
s.accounts = make(map[common.Hash][]byte)
|
||||
s.storages = make(map[common.Hash]map[common.Hash][]byte)
|
||||
s.accountsOrigin = make(map[common.Hash][]byte)
|
||||
s.storagesOrigin = make(map[common.Hash]map[common.Hash][]byte)
|
||||
s.stateObjectsDirty = make(map[common.Address]struct{})
|
||||
s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount)
|
||||
return root, nil
|
||||
}
|
||||
|
||||
|
@ -1206,7 +1400,7 @@ func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addre
|
|||
}
|
||||
|
||||
// convertAccountSet converts a provided account set from address keyed to hash keyed.
|
||||
func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.Hash]struct{} {
|
||||
func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount) map[common.Hash]struct{} {
|
||||
ret := make(map[common.Hash]struct{}, len(set))
|
||||
for addr := range set {
|
||||
obj, exist := s.stateObjects[addr]
|
||||
|
@ -1218,3 +1412,24 @@ func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.
|
|||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// copyAccounts returns a deep-copied account set of the provided one.
|
||||
func copyAccounts(set map[common.Hash][]byte) map[common.Hash][]byte {
|
||||
copied := make(map[common.Hash][]byte, len(set))
|
||||
for key, val := range set {
|
||||
copied[key] = common.CopyBytes(val)
|
||||
}
|
||||
return copied
|
||||
}
|
||||
|
||||
// copyStorages returns a deep-copied storage set of the provided one.
|
||||
func copyStorages(set map[common.Hash]map[common.Hash][]byte) map[common.Hash]map[common.Hash][]byte {
|
||||
copied := make(map[common.Hash]map[common.Hash][]byte, len(set))
|
||||
for addr, subset := range set {
|
||||
copied[addr] = make(map[common.Hash][]byte, len(subset))
|
||||
for key, val := range subset {
|
||||
copied[addr][key] = common.CopyBytes(val)
|
||||
}
|
||||
}
|
||||
return copied
|
||||
}
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||
)
|
||||
|
||||
// A stateTest checks that the state changes are correctly captured. Instances
|
||||
// of this test with pseudorandom content are created by Generate.
|
||||
//
|
||||
// The test works as follows:
|
||||
//
|
||||
// A list of states are created by applying actions. The state changes between
|
||||
// each state instance are tracked and be verified.
|
||||
type stateTest struct {
|
||||
addrs []common.Address // all account addresses
|
||||
actions [][]testAction // modifications to the state, grouped by block
|
||||
chunk int // The number of actions per chunk
|
||||
err error // failure details are reported through this field
|
||||
}
|
||||
|
||||
// newStateTestAction creates a random action that changes state.
|
||||
func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction {
|
||||
actions := []testAction{
|
||||
{
|
||||
name: "SetBalance",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SetBalance(addr, big.NewInt(a.args[0]), 0)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "SetNonce",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SetNonce(addr, uint64(a.args[0]))
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "SetState",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
var key, val common.Hash
|
||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||
binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
|
||||
s.SetState(addr, key, val)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
{
|
||||
name: "SetCode",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
code := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||
s.SetCode(addr, code)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
{
|
||||
name: "CreateAccount",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.CreateAccount(addr)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Selfdestruct",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SelfDestruct(addr)
|
||||
},
|
||||
},
|
||||
}
|
||||
var nonRandom = index != -1
|
||||
if index == -1 {
|
||||
index = r.Intn(len(actions))
|
||||
}
|
||||
action := actions[index]
|
||||
var names []string
|
||||
if !action.noAddr {
|
||||
names = append(names, addr.Hex())
|
||||
}
|
||||
for i := range action.args {
|
||||
if nonRandom {
|
||||
action.args[i] = rand.Int63n(10000) + 1 // set balance to non-zero
|
||||
} else {
|
||||
action.args[i] = rand.Int63n(10000)
|
||||
}
|
||||
names = append(names, fmt.Sprint(action.args[i]))
|
||||
}
|
||||
action.name += " " + strings.Join(names, ", ")
|
||||
return action
|
||||
}
|
||||
|
||||
// Generate returns a new snapshot test of the given size. All randomness is
|
||||
// derived from r.
|
||||
func (*stateTest) Generate(r *rand.Rand, size int) reflect.Value {
|
||||
addrs := make([]common.Address, 5)
|
||||
for i := range addrs {
|
||||
addrs[i][0] = byte(i)
|
||||
}
|
||||
actions := make([][]testAction, rand.Intn(5)+1)
|
||||
|
||||
for i := 0; i < len(actions); i++ {
|
||||
actions[i] = make([]testAction, size)
|
||||
for j := range actions[i] {
|
||||
if j == 0 {
|
||||
// Always include a set balance action to make sure
|
||||
// the state changes are not empty.
|
||||
actions[i][j] = newStateTestAction(common.HexToAddress("0xdeadbeef"), r, 0)
|
||||
continue
|
||||
}
|
||||
actions[i][j] = newStateTestAction(addrs[r.Intn(len(addrs))], r, -1)
|
||||
}
|
||||
}
|
||||
chunk := int(math.Sqrt(float64(size)))
|
||||
if size > 0 && chunk == 0 {
|
||||
chunk = 1
|
||||
}
|
||||
return reflect.ValueOf(&stateTest{
|
||||
addrs: addrs,
|
||||
actions: actions,
|
||||
chunk: chunk,
|
||||
})
|
||||
}
|
||||
|
||||
func (test *stateTest) String() string {
|
||||
out := new(bytes.Buffer)
|
||||
for i, actions := range test.actions {
|
||||
fmt.Fprintf(out, "---- block %d ----\n", i)
|
||||
for j, action := range actions {
|
||||
if j%test.chunk == 0 {
|
||||
fmt.Fprintf(out, "---- transaction %d ----\n", j/test.chunk)
|
||||
}
|
||||
fmt.Fprintf(out, "%4d: %s\n", j%test.chunk, action.name)
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (test *stateTest) run() bool {
|
||||
var (
|
||||
roots []common.Hash
|
||||
accountList []map[common.Hash][]byte
|
||||
storageList []map[common.Hash]map[common.Hash][]byte
|
||||
onCommit = func(states *triestate.Set) {
|
||||
accountList = append(accountList, copyAccounts(states.Accounts))
|
||||
storageList = append(storageList, copyStorages(states.Storages))
|
||||
}
|
||||
disk = rawdb.NewMemoryDatabase()
|
||||
tdb = trie.NewDatabaseWithConfig(disk, &trie.Config{OnCommit: onCommit})
|
||||
sdb = NewDatabaseWithNodeDB(disk, tdb)
|
||||
byzantium = rand.Intn(2) == 0
|
||||
)
|
||||
for i, actions := range test.actions {
|
||||
root := types.EmptyRootHash
|
||||
if i != 0 {
|
||||
root = roots[len(roots)-1]
|
||||
}
|
||||
state, err := New(root, sdb, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i, action := range actions {
|
||||
if i%test.chunk == 0 && i != 0 {
|
||||
if byzantium {
|
||||
state.Finalise(true) // call finalise at the transaction boundary
|
||||
} else {
|
||||
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
|
||||
}
|
||||
}
|
||||
action.fn(action, state)
|
||||
}
|
||||
if byzantium {
|
||||
state.Finalise(true) // call finalise at the transaction boundary
|
||||
} else {
|
||||
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
|
||||
}
|
||||
nroot, err := state.Commit(0, true) // call commit at the block boundary
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if nroot == root {
|
||||
return true // filter out non-change state transition
|
||||
}
|
||||
roots = append(roots, nroot)
|
||||
}
|
||||
for i := 0; i < len(test.actions); i++ {
|
||||
root := types.EmptyRootHash
|
||||
if i != 0 {
|
||||
root = roots[i-1]
|
||||
}
|
||||
test.err = test.verify(root, roots[i], tdb, accountList[i], storageList[i])
|
||||
if test.err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// verifyAccountCreation this function is called once the state diff says that
|
||||
// specific account was not present. A serial of checks will be performed to
|
||||
// ensure the state diff is correct, includes:
|
||||
//
|
||||
// - the account was indeed not present in trie
|
||||
// - the account is present in new trie, nil->nil is regarded as invalid
|
||||
// - the slots transition is correct
|
||||
func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addrHash common.Hash, slots map[common.Hash][]byte) error {
|
||||
// Verify account change
|
||||
oBlob, err := otr.Get(addrHash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nBlob, err := ntr.Get(addrHash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(oBlob) != 0 {
|
||||
return fmt.Errorf("unexpected account in old trie, %x", addrHash)
|
||||
}
|
||||
if len(nBlob) == 0 {
|
||||
return fmt.Errorf("missing account in new trie, %x", addrHash)
|
||||
}
|
||||
|
||||
// Verify storage changes
|
||||
var nAcct types.StateAccount
|
||||
if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil {
|
||||
return err
|
||||
}
|
||||
// Account has no slot, empty slot set is expected
|
||||
if nAcct.Root == types.EmptyRootHash {
|
||||
if len(slots) != 0 {
|
||||
return fmt.Errorf("unexpected slot changes %x", addrHash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Account has slots, ensure all new slots are contained
|
||||
st, err := trie.New(trie.StorageTrieID(next, addrHash, nAcct.Root), db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, val := range slots {
|
||||
st.Update(key.Bytes(), val)
|
||||
}
|
||||
if st.Hash() != types.EmptyRootHash {
|
||||
return errors.New("invalid slot changes")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyAccountUpdate this function is called once the state diff says that
|
||||
// specific account was present. A serial of checks will be performed to
|
||||
// ensure the state diff is correct, includes:
|
||||
//
|
||||
// - the account was indeed present in trie
|
||||
// - the account in old trie matches the provided value
|
||||
// - the slots transition is correct
|
||||
func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addrHash common.Hash, origin []byte, slots map[common.Hash][]byte) error {
|
||||
// Verify account change
|
||||
oBlob, err := otr.Get(addrHash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nBlob, err := ntr.Get(addrHash.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(oBlob) == 0 {
|
||||
return fmt.Errorf("missing account in old trie, %x", addrHash)
|
||||
}
|
||||
full, err := types.FullAccountRLP(origin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(full, oBlob) {
|
||||
return fmt.Errorf("account value is not matched, %x", addrHash)
|
||||
}
|
||||
|
||||
// Decode accounts
|
||||
var (
|
||||
oAcct types.StateAccount
|
||||
nAcct types.StateAccount
|
||||
nRoot common.Hash
|
||||
)
|
||||
if err := rlp.DecodeBytes(oBlob, &oAcct); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(nBlob) == 0 {
|
||||
nRoot = types.EmptyRootHash
|
||||
} else {
|
||||
if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil {
|
||||
return err
|
||||
}
|
||||
nRoot = nAcct.Root
|
||||
}
|
||||
|
||||
// Verify storage
|
||||
st, err := trie.New(trie.StorageTrieID(next, addrHash, nRoot), db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, val := range slots {
|
||||
st.Update(key.Bytes(), val)
|
||||
}
|
||||
if st.Hash() != oAcct.Root {
|
||||
return errors.New("invalid slot changes")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Database, accountsOrigin map[common.Hash][]byte, storagesOrigin map[common.Hash]map[common.Hash][]byte) error {
|
||||
otr, err := trie.New(trie.StateTrieID(root), db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ntr, err := trie.New(trie.StateTrieID(next), db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for addrHash, account := range accountsOrigin {
|
||||
var err error
|
||||
if len(account) == 0 {
|
||||
err = test.verifyAccountCreation(next, db, otr, ntr, addrHash, storagesOrigin[addrHash])
|
||||
} else {
|
||||
err = test.verifyAccountUpdate(next, db, otr, ntr, addrHash, accountsOrigin[addrHash], storagesOrigin[addrHash])
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestStateChanges(t *testing.T) {
|
||||
config := &quick.Config{MaxCount: 1000}
|
||||
err := quick.Check((*stateTest).run, config)
|
||||
if cerr, ok := err.(*quick.CheckError); ok {
|
||||
test := cerr.In[0].(*stateTest)
|
||||
t.Errorf("%v:\n%s", test.err, test)
|
||||
} else if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package state
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
@ -105,7 +106,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
|||
}
|
||||
|
||||
// Commit and cross check the databases.
|
||||
transRoot, err := transState.Commit(false)
|
||||
transRoot, err := transState.Commit(0, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit transition state: %v", err)
|
||||
}
|
||||
|
@ -113,7 +114,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
|||
t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
|
||||
}
|
||||
|
||||
finalRoot, err := finalState.Commit(false)
|
||||
finalRoot, err := finalState.Commit(0, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit final state: %v", err)
|
||||
}
|
||||
|
@ -300,9 +301,9 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "Suicide",
|
||||
name: "SelfDestruct",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.Suicide(addr)
|
||||
s.SelfDestruct(addr)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -452,7 +453,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
|||
}
|
||||
// Check basic accessor methods.
|
||||
checkeq("Exist", state.Exist(addr), checkstate.Exist(addr))
|
||||
checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr))
|
||||
checkeq("HasSelfdestructed", state.HasSelfDestructed(addr), checkstate.HasSelfDestructed(addr))
|
||||
checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr))
|
||||
checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr))
|
||||
checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr))
|
||||
|
@ -484,9 +485,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
|||
}
|
||||
|
||||
func TestTouchDelete(t *testing.T) {
|
||||
s := newStateTest()
|
||||
s := newStateEnv()
|
||||
s.state.GetOrNewStateObject(common.Address{})
|
||||
root, _ := s.state.Commit(false)
|
||||
root, _ := s.state.Commit(0, false)
|
||||
s.state, _ = New(root, s.state.db, s.state.snaps)
|
||||
|
||||
snapshot := s.state.Snapshot()
|
||||
|
@ -521,7 +522,8 @@ func TestCopyOfCopy(t *testing.T) {
|
|||
//
|
||||
// See https://github.com/ethereum/go-ethereum/issues/20106.
|
||||
func TestCopyCommitCopy(t *testing.T) {
|
||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
tdb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
state, _ := New(types.EmptyRootHash, tdb, nil)
|
||||
|
||||
// Create an account and check if the retrieved balance is correct
|
||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||
|
@ -558,20 +560,6 @@ func TestCopyCommitCopy(t *testing.T) {
|
|||
if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{})
|
||||
}
|
||||
|
||||
copyOne.Commit(false)
|
||||
if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
t.Fatalf("first copy post-commit balance mismatch: have %v, want %v", balance, 42)
|
||||
}
|
||||
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
|
||||
t.Fatalf("first copy post-commit code mismatch: have %x, want %x", code, []byte("hello"))
|
||||
}
|
||||
if val := copyOne.GetState(addr, skey); val != sval {
|
||||
t.Fatalf("first copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
if val := copyOne.GetCommittedState(addr, skey); val != sval {
|
||||
t.Fatalf("first copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
// Copy the copy and check the balance once more
|
||||
copyTwo := copyOne.Copy()
|
||||
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
|
@ -583,8 +571,23 @@ func TestCopyCommitCopy(t *testing.T) {
|
|||
if val := copyTwo.GetState(addr, skey); val != sval {
|
||||
t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
if val := copyTwo.GetCommittedState(addr, skey); val != sval {
|
||||
t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
// Commit state, ensure states can be loaded from disk
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, tdb, nil)
|
||||
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
|
||||
}
|
||||
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
|
||||
t.Fatalf("state post-commit code mismatch: have %x, want %x", code, []byte("hello"))
|
||||
}
|
||||
if val := state.GetState(addr, skey); val != sval {
|
||||
t.Fatalf("state post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
if val := state.GetCommittedState(addr, skey); val != sval {
|
||||
t.Fatalf("state post-commit committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -644,19 +647,6 @@ func TestCopyCopyCommitCopy(t *testing.T) {
|
|||
if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{})
|
||||
}
|
||||
copyTwo.Commit(false)
|
||||
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42)
|
||||
}
|
||||
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
|
||||
t.Fatalf("second copy post-commit code mismatch: have %x, want %x", code, []byte("hello"))
|
||||
}
|
||||
if val := copyTwo.GetState(addr, skey); val != sval {
|
||||
t.Fatalf("second copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
if val := copyTwo.GetCommittedState(addr, skey); val != sval {
|
||||
t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
// Copy the copy-copy and check the balance once more
|
||||
copyThree := copyTwo.Copy()
|
||||
if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
|
@ -668,11 +658,56 @@ func TestCopyCopyCommitCopy(t *testing.T) {
|
|||
if val := copyThree.GetState(addr, skey); val != sval {
|
||||
t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
if val := copyThree.GetCommittedState(addr, skey); val != sval {
|
||||
if val := copyThree.GetCommittedState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCommitCopy tests the copy from a committed state is not functional.
|
||||
func TestCommitCopy(t *testing.T) {
|
||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
|
||||
// Create an account and check if the retrieved balance is correct
|
||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||
skey := common.HexToHash("aaa")
|
||||
sval := common.HexToHash("bbb")
|
||||
|
||||
state.SetBalance(addr, big.NewInt(42), 0) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetState(addr, skey, sval) // Change the storage trie
|
||||
|
||||
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
|
||||
}
|
||||
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
|
||||
t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello"))
|
||||
}
|
||||
if val := state.GetState(addr, skey); val != sval {
|
||||
t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{})
|
||||
}
|
||||
// Copy the committed state database, the copied one is not functional.
|
||||
state.Commit(0, true)
|
||||
copied := state.Copy()
|
||||
if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Fatalf("unexpected balance: have %v", balance)
|
||||
}
|
||||
if code := copied.GetCode(addr); code != nil {
|
||||
t.Fatalf("unexpected code: have %x", code)
|
||||
}
|
||||
if val := copied.GetState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("unexpected storage slot: have %x", val)
|
||||
}
|
||||
if val := copied.GetCommittedState(addr, skey); val != (common.Hash{}) {
|
||||
t.Fatalf("unexpected storage slot: have %x", val)
|
||||
}
|
||||
if !errors.Is(copied.Error(), trie.ErrCommitted) {
|
||||
t.Fatalf("unexpected state error, %v", copied.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
||||
// while changing the internals of StateDB. The workflow is that a contract is
|
||||
// self-destructed, then in a follow-up transaction (but same block) it's created
|
||||
|
@ -688,11 +723,11 @@ func TestDeleteCreateRevert(t *testing.T) {
|
|||
addr := common.BytesToAddress([]byte("so"))
|
||||
state.SetBalance(addr, big.NewInt(1), 0x0)
|
||||
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
||||
// Simulate self-destructing in one transaction, then create-reverting in another
|
||||
state.Suicide(addr)
|
||||
state.SelfDestruct(addr)
|
||||
state.Finalise(true)
|
||||
|
||||
id := state.Snapshot()
|
||||
|
@ -700,7 +735,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
|||
state.RevertToSnapshot(id)
|
||||
|
||||
// Commit the entire state and make sure we don't crash and have the correct state
|
||||
root, _ = state.Commit(true)
|
||||
root, _ = state.Commit(0, true)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
||||
if state.getStateObject(addr) != nil {
|
||||
|
@ -724,7 +759,7 @@ func TestMissingTrieNodes(t *testing.T) {
|
|||
a2 := common.BytesToAddress([]byte("another"))
|
||||
state.SetBalance(a2, big.NewInt(100), 0x0)
|
||||
state.SetCode(a2, []byte{1, 2, 4})
|
||||
root, _ = state.Commit(false)
|
||||
root, _ = state.Commit(0, false)
|
||||
t.Logf("root: %x", root)
|
||||
// force-flush
|
||||
state.Database().TrieDB().Cap(0)
|
||||
|
@ -748,7 +783,7 @@ func TestMissingTrieNodes(t *testing.T) {
|
|||
}
|
||||
// Modify the state
|
||||
state.SetBalance(addr, big.NewInt(2), 0x0)
|
||||
root, err := state.Commit(false)
|
||||
root, err := state.Commit(0, false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got root :%x", root)
|
||||
}
|
||||
|
@ -943,7 +978,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
|
|||
state.SetState(common.Address{a}, common.Hash{a, s}, common.Hash{a, s})
|
||||
}
|
||||
}
|
||||
root, err := state.Commit(false)
|
||||
root, err := state.Commit(0, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit state trie: %v", err)
|
||||
}
|
||||
|
@ -1022,7 +1057,7 @@ func TestResetObject(t *testing.T) {
|
|||
state.CreateAccount(addr)
|
||||
state.SetBalance(addr, big.NewInt(2), 0x0)
|
||||
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
|
||||
root, _ := state.Commit(true)
|
||||
root, _ := state.Commit(0, true)
|
||||
|
||||
// Ensure the original account is wiped properly
|
||||
snap := snaps.Snapshot(root)
|
||||
|
|
|
@ -42,7 +42,7 @@ type testAccount struct {
|
|||
func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) {
|
||||
// Create an empty state
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
sdb := NewDatabase(db)
|
||||
sdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
||||
state, _ := New(types.EmptyRootHash, sdb, nil)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
|
@ -64,13 +64,13 @@ func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) {
|
|||
if i%5 == 0 {
|
||||
for j := byte(0); j < 5; j++ {
|
||||
hash := crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j})
|
||||
obj.SetState(sdb, hash, hash)
|
||||
obj.SetState(hash, hash)
|
||||
}
|
||||
}
|
||||
state.updateStateObject(obj)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
|
||||
// Return the generated state
|
||||
return db, sdb, root, accounts
|
||||
|
@ -100,28 +100,9 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
|
|||
}
|
||||
}
|
||||
|
||||
// checkTrieConsistency checks that all nodes in a (sub-)trie are indeed present.
|
||||
func checkTrieConsistency(db ethdb.Database, root common.Hash) error {
|
||||
if v, _ := db.Get(root[:]); v == nil {
|
||||
return nil // Consider a non existent state consistent.
|
||||
}
|
||||
trie, err := trie.New(trie.StateTrieID(root), trie.NewDatabase(db))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it := trie.NodeIterator(nil)
|
||||
for it.Next(true) {
|
||||
}
|
||||
return it.Error()
|
||||
}
|
||||
|
||||
// checkStateConsistency checks that all data of a state root is present.
|
||||
func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||
// Create and iterate a state trie rooted in a sub-node
|
||||
if _, err := db.Get(root.Bytes()); err != nil {
|
||||
return nil // Consider a non existent state consistent.
|
||||
}
|
||||
state, err := New(root, NewDatabase(db), nil)
|
||||
state, err := New(root, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -171,7 +152,7 @@ type stateElement struct {
|
|||
|
||||
func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
||||
// Create a random state to copy
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
srcDisk, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
if commit {
|
||||
srcDb.TrieDB().Commit(srcRoot, false)
|
||||
}
|
||||
|
@ -204,7 +185,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
|||
codeResults = make([]trie.CodeSyncResult, len(codeElements))
|
||||
)
|
||||
for i, element := range codeElements {
|
||||
data, err := srcDb.ContractCode(common.Hash{}, element.code)
|
||||
data, err := srcDb.ContractCode(common.Address{}, element.code)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code)
|
||||
}
|
||||
|
@ -274,6 +255,10 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
|||
})
|
||||
}
|
||||
}
|
||||
// Copy the preimages from source db in order to traverse the state.
|
||||
srcDb.TrieDB().WritePreimages()
|
||||
copyPreimages(srcDisk, dstDb)
|
||||
|
||||
// Cross check that the two states are in sync
|
||||
checkStateAccounts(t, dstDb, srcRoot, srcAccounts)
|
||||
}
|
||||
|
@ -282,7 +267,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
|||
// partial results are returned, and the others sent only later.
|
||||
func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
srcDisk, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
|
@ -312,7 +297,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
|||
if len(codeElements) > 0 {
|
||||
codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1)
|
||||
for i, element := range codeElements[:len(codeResults)] {
|
||||
data, err := srcDb.ContractCode(common.Hash{}, element.code)
|
||||
data, err := srcDb.ContractCode(common.Address{}, element.code)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve contract bytecode for %x", element.code)
|
||||
}
|
||||
|
@ -363,6 +348,10 @@ func TestIterativeDelayedStateSync(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
// Copy the preimages from source db in order to traverse the state.
|
||||
srcDb.TrieDB().WritePreimages()
|
||||
copyPreimages(srcDisk, dstDb)
|
||||
|
||||
// Cross check that the two states are in sync
|
||||
checkStateAccounts(t, dstDb, srcRoot, srcAccounts)
|
||||
}
|
||||
|
@ -375,7 +364,7 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
|
|||
|
||||
func testIterativeRandomStateSync(t *testing.T, count int) {
|
||||
// Create a random state to copy
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
srcDisk, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
|
@ -399,7 +388,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
|
|||
if len(codeQueue) > 0 {
|
||||
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||
for hash := range codeQueue {
|
||||
data, err := srcDb.ContractCode(common.Hash{}, hash)
|
||||
data, err := srcDb.ContractCode(common.Address{}, hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
|
@ -447,6 +436,10 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
|
|||
codeQueue[hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
// Copy the preimages from source db in order to traverse the state.
|
||||
srcDb.TrieDB().WritePreimages()
|
||||
copyPreimages(srcDisk, dstDb)
|
||||
|
||||
// Cross check that the two states are in sync
|
||||
checkStateAccounts(t, dstDb, srcRoot, srcAccounts)
|
||||
}
|
||||
|
@ -455,7 +448,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
|
|||
// partial results are returned (Even those randomly), others sent only later.
|
||||
func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
srcDisk, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
|
@ -481,7 +474,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
|||
for hash := range codeQueue {
|
||||
delete(codeQueue, hash)
|
||||
|
||||
data, err := srcDb.ContractCode(common.Hash{}, hash)
|
||||
data, err := srcDb.ContractCode(common.Address{}, hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
|
@ -537,6 +530,10 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
|||
codeQueue[hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
// Copy the preimages from source db in order to traverse the state.
|
||||
srcDb.TrieDB().WritePreimages()
|
||||
copyPreimages(srcDisk, dstDb)
|
||||
|
||||
// Cross check that the two states are in sync
|
||||
checkStateAccounts(t, dstDb, srcRoot, srcAccounts)
|
||||
}
|
||||
|
@ -555,7 +552,6 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
}
|
||||
}
|
||||
isCode[types.EmptyCodeHash] = struct{}{}
|
||||
checkTrieConsistency(db, srcRoot)
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
|
@ -566,6 +562,10 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
addedPaths []string
|
||||
addedHashes []common.Hash
|
||||
)
|
||||
reader, err := srcDb.TrieDB().Reader(srcRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("state is not available %x", srcRoot)
|
||||
}
|
||||
nodeQueue := make(map[string]stateElement)
|
||||
codeQueue := make(map[common.Hash]struct{})
|
||||
paths, nodes, codes := sched.Missing(1)
|
||||
|
@ -584,7 +584,7 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
if len(codeQueue) > 0 {
|
||||
results := make([]trie.CodeSyncResult, 0, len(codeQueue))
|
||||
for hash := range codeQueue {
|
||||
data, err := srcDb.ContractCode(common.Hash{}, hash)
|
||||
data, err := srcDb.ContractCode(common.Address{}, hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x", hash)
|
||||
}
|
||||
|
@ -598,12 +598,11 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
var nodehashes []common.Hash
|
||||
if len(nodeQueue) > 0 {
|
||||
results := make([]trie.NodeSyncResult, 0, len(nodeQueue))
|
||||
for path, element := range nodeQueue {
|
||||
owner, inner := trie.ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.TrieDB().Reader(srcRoot).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x", element.hash)
|
||||
}
|
||||
|
@ -613,7 +612,6 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
addedPaths = append(addedPaths, element.path)
|
||||
addedHashes = append(addedHashes, element.hash)
|
||||
}
|
||||
nodehashes = append(nodehashes, element.hash)
|
||||
}
|
||||
// Process each of the state nodes
|
||||
for _, result := range results {
|
||||
|
@ -628,13 +626,6 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
}
|
||||
batch.Write()
|
||||
|
||||
for _, root := range nodehashes {
|
||||
// Can't use checkStateConsistency here because subtrie keys may have odd
|
||||
// length and crash in LeafKey.
|
||||
if err := checkTrieConsistency(dstDb, root); err != nil {
|
||||
t.Fatalf("state inconsistent: %v", err)
|
||||
}
|
||||
}
|
||||
// Fetch the next batch to retrieve
|
||||
nodeQueue = make(map[string]stateElement)
|
||||
codeQueue = make(map[common.Hash]struct{})
|
||||
|
@ -650,6 +641,10 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
codeQueue[hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
// Copy the preimages from source db in order to traverse the state.
|
||||
srcDb.TrieDB().WritePreimages()
|
||||
copyPreimages(db, dstDb)
|
||||
|
||||
// Sanity check that removing any node from the database is detected
|
||||
for _, node := range addedCodes {
|
||||
val := rawdb.ReadCode(dstDb, node)
|
||||
|
@ -674,3 +669,15 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
rawdb.WriteTrieNode(dstDb, owner, inner, hash, val, scheme)
|
||||
}
|
||||
}
|
||||
|
||||
func copyPreimages(srcDb, dstDb ethdb.Database) {
|
||||
it := srcDb.NewIterator(rawdb.PreimagePrefix, nil)
|
||||
defer it.Release()
|
||||
|
||||
preimages := make(map[common.Hash][]byte)
|
||||
for it.Next() {
|
||||
hash := it.Key()[len(rawdb.PreimagePrefix):]
|
||||
preimages[common.BytesToHash(hash)] = common.CopyBytes(it.Value())
|
||||
}
|
||||
rawdb.WritePreimages(dstDb, preimages)
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ func (sf *subfetcher) loop() {
|
|||
}
|
||||
sf.trie = trie
|
||||
} else {
|
||||
trie, err := sf.db.OpenStorageTrie(sf.state, sf.owner, sf.root)
|
||||
trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root)
|
||||
if err != nil {
|
||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
||||
return
|
||||
|
|
|
@ -173,6 +173,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
|||
}
|
||||
// Create a new context to be used in the EVM environment
|
||||
blockContext := NewEVMBlockContext(header, bc, author)
|
||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
|
||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.BlobHashes()}, statedb, config, cfg)
|
||||
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
|
@ -45,19 +46,23 @@ func u64(val uint64) *uint64 { return &val }
|
|||
func TestStateProcessorErrors(t *testing.T) {
|
||||
var (
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
Ethash: new(params.EthashConfig),
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
Ethash: new(params.EthashConfig),
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
ShanghaiTime: new(uint64),
|
||||
CancunTime: new(uint64),
|
||||
}
|
||||
signer = types.LatestSigner(config)
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
@ -89,6 +94,22 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
}), signer, key1)
|
||||
return tx
|
||||
}
|
||||
var mkBlobTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, hashes []common.Hash) *types.Transaction {
|
||||
tx, err := types.SignTx(types.NewTx(&types.BlobTx{
|
||||
Nonce: nonce,
|
||||
GasTipCap: uint256.MustFromBig(gasTipCap),
|
||||
GasFeeCap: uint256.MustFromBig(gasFeeCap),
|
||||
Gas: gasLimit,
|
||||
To: to,
|
||||
BlobHashes: hashes,
|
||||
Value: new(uint256.Int),
|
||||
}), signer, key1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
{ // Tests against a 'recent' chain definition
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
|
@ -105,8 +126,10 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||
)
|
||||
|
||||
defer blockchain.Stop()
|
||||
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
tooBigNumber := new(big.Int).Set(bigNumber)
|
||||
|
@ -209,8 +232,26 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
|
||||
},
|
||||
{ // ErrMaxInitCodeSizeExceeded
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicCreationTx(0, 500000, common.Big0, big.NewInt(params.InitialBaseFee), tooBigInitCode[:]),
|
||||
},
|
||||
want: "could not apply tx 0 [0xd491405f06c92d118dd3208376fcee18a57c54bc52063ee4a26b1cf296857c25]: max initcode size exceeded: code size 49153 limit 49152",
|
||||
},
|
||||
{ // ErrIntrinsicGas: Not enough gas to cover init code
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicCreationTx(0, 54299, common.Big0, big.NewInt(params.InitialBaseFee), make([]byte, 320)),
|
||||
},
|
||||
want: "could not apply tx 0 [0xfd49536a9b323769d8472fcb3ebb3689b707a349379baee3e2ee3fe7baae06a1]: intrinsic gas too low: have 54299, want 54300",
|
||||
},
|
||||
{ // ErrBlobFeeCapTooLow
|
||||
txs: []*types.Transaction{
|
||||
mkBlobTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(1), []common.Hash{(common.Hash{1})}),
|
||||
},
|
||||
want: "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 1 baseFee: 875000000",
|
||||
},
|
||||
} {
|
||||
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
||||
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
|
@ -284,7 +325,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
|
@ -298,73 +339,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
||||
},
|
||||
} {
|
||||
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
}
|
||||
if have, want := err.Error(), tt.want; have != want {
|
||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrMaxInitCodeSizeExceeded, for this we need extra Shanghai (EIP-3860) enabled.
|
||||
{
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
ArrowGlacierBlock: big.NewInt(0),
|
||||
GrayGlacierBlock: big.NewInt(0),
|
||||
MergeNetsplitBlock: big.NewInt(0),
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
ShanghaiTime: u64(0),
|
||||
},
|
||||
Alloc: GenesisAlloc{
|
||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||
Nonce: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||
smallInitCode = [320]byte{}
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
txs []*types.Transaction
|
||||
want string
|
||||
}{
|
||||
{ // ErrMaxInitCodeSizeExceeded
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]),
|
||||
},
|
||||
want: "could not apply tx 0 [0x832b54a6c3359474a9f504b1003b2cc1b6fcaa18e4ef369eb45b5d40dad6378f]: max initcode size exceeded: code size 49153 limit 49152",
|
||||
},
|
||||
{ // ErrIntrinsicGas: Not enough gas to cover init code
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]),
|
||||
},
|
||||
want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
|
||||
},
|
||||
} {
|
||||
block := GenerateBadBlock(genesis, beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
||||
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
|
@ -412,6 +387,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(header.Number.Bytes())
|
||||
var cumulativeGas uint64
|
||||
var nBlobs int
|
||||
for _, tx := range txs {
|
||||
txh := tx.Hash()
|
||||
hasher.Write(txh[:])
|
||||
|
@ -420,8 +396,20 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||
receipt.GasUsed = tx.Gas()
|
||||
receipts = append(receipts, receipt)
|
||||
cumulativeGas += tx.Gas()
|
||||
nBlobs += len(tx.BlobHashes())
|
||||
}
|
||||
header.Root = common.BytesToHash(hasher.Sum(nil))
|
||||
if config.IsCancun(header.Number, header.Time) {
|
||||
var pExcess, pUsed = uint64(0), uint64(0)
|
||||
if parent.ExcessDataGas() != nil {
|
||||
pExcess = *parent.ExcessDataGas()
|
||||
pUsed = *parent.DataGasUsed()
|
||||
}
|
||||
excess := misc.CalcExcessDataGas(pExcess, pUsed)
|
||||
used := uint64(nBlobs * params.BlobTxDataGasPerBlob)
|
||||
header.ExcessDataGas = &excess
|
||||
header.DataGasUsed = &used
|
||||
}
|
||||
// Assemble and return the final block for sealing
|
||||
if config.IsShanghai(header.Number, header.Time) {
|
||||
return types.NewBlockWithWithdrawals(header, txs, nil, receipts, []*types.Withdrawal{}, trie.NewStackTrie(nil))
|
||||
|
|
|
@ -17,12 +17,14 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
@ -126,17 +128,18 @@ func toWordSize(size uint64) uint64 {
|
|||
// A Message contains the data derived from a single transaction that is relevant to state
|
||||
// processing.
|
||||
type Message struct {
|
||||
To *common.Address
|
||||
From common.Address
|
||||
Nonce uint64
|
||||
Value *big.Int
|
||||
GasLimit uint64
|
||||
GasPrice *big.Int
|
||||
GasFeeCap *big.Int
|
||||
GasTipCap *big.Int
|
||||
Data []byte
|
||||
AccessList types.AccessList
|
||||
BlobHashes []common.Hash
|
||||
To *common.Address
|
||||
From common.Address
|
||||
Nonce uint64
|
||||
Value *big.Int
|
||||
GasLimit uint64
|
||||
GasPrice *big.Int
|
||||
GasFeeCap *big.Int
|
||||
GasTipCap *big.Int
|
||||
Data []byte
|
||||
AccessList types.AccessList
|
||||
BlobGasFeeCap *big.Int
|
||||
BlobHashes []common.Hash
|
||||
|
||||
// When SkipAccountChecks is true, the message nonce is not checked against the
|
||||
// account nonce in state. It also disables checking that the sender is an EOA.
|
||||
|
@ -158,6 +161,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
|||
AccessList: tx.AccessList(),
|
||||
SkipAccountChecks: false,
|
||||
BlobHashes: tx.BlobHashes(),
|
||||
BlobGasFeeCap: tx.BlobGasFeeCap(),
|
||||
}
|
||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||
if baseFee != nil {
|
||||
|
@ -231,12 +235,28 @@ func (st *StateTransition) to() common.Address {
|
|||
func (st *StateTransition) buyGas() error {
|
||||
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
|
||||
mgval = mgval.Mul(mgval, st.msg.GasPrice)
|
||||
balanceCheck := mgval
|
||||
balanceCheck := new(big.Int).Set(mgval)
|
||||
if st.msg.GasFeeCap != nil {
|
||||
balanceCheck = new(big.Int).SetUint64(st.msg.GasLimit)
|
||||
balanceCheck.SetUint64(st.msg.GasLimit)
|
||||
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
|
||||
balanceCheck.Add(balanceCheck, st.msg.Value)
|
||||
}
|
||||
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||
if dataGas := st.dataGasUsed(); dataGas > 0 {
|
||||
if st.evm.Context.ExcessDataGas == nil {
|
||||
// programming error
|
||||
panic("missing field excess data gas")
|
||||
}
|
||||
// Check that the user has enough funds to cover dataGasUsed * tx.BlobGasFeeCap
|
||||
blobBalanceCheck := new(big.Int).SetUint64(dataGas)
|
||||
blobBalanceCheck.Mul(blobBalanceCheck, st.msg.BlobGasFeeCap)
|
||||
balanceCheck.Add(balanceCheck, blobBalanceCheck)
|
||||
// Pay for dataGasUsed * actual blob fee
|
||||
blobFee := new(big.Int).SetUint64(dataGas)
|
||||
blobFee.Mul(blobFee, misc.CalcBlobFee(*st.evm.Context.ExcessDataGas))
|
||||
mgval.Add(mgval, blobFee)
|
||||
}
|
||||
}
|
||||
if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||
}
|
||||
|
@ -298,6 +318,28 @@ func (st *StateTransition) preCheck() error {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Check the blob version validity
|
||||
if msg.BlobHashes != nil {
|
||||
if len(msg.BlobHashes) == 0 {
|
||||
return errors.New("blob transaction missing blob hashes")
|
||||
}
|
||||
for i, hash := range msg.BlobHashes {
|
||||
if hash[0] != params.BlobTxHashVersion {
|
||||
return fmt.Errorf("blob %d hash version mismatch (have %d, supported %d)",
|
||||
i, hash[0], params.BlobTxHashVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||
if st.dataGasUsed() > 0 {
|
||||
// Check that the user is paying at least the current blob fee
|
||||
if have, want := st.msg.BlobGasFeeCap, misc.CalcBlobFee(*st.evm.Context.ExcessDataGas); have.Cmp(want) < 0 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrBlobFeeCapTooLow, st.msg.From.Hex(), have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return st.buyGas()
|
||||
}
|
||||
|
||||
|
@ -424,3 +466,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
|
|||
func (st *StateTransition) gasUsed() uint64 {
|
||||
return st.initialGas - st.gasRemaining
|
||||
}
|
||||
|
||||
// dataGasUsed returns the amount of data gas used by the message.
|
||||
func (st *StateTransition) dataGasUsed() uint64 {
|
||||
return uint64(len(st.msg.BlobHashes) * params.BlobTxDataGasPerBlob)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package txpool
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrAlreadyKnown is returned if the transactions is already contained
|
||||
// within the pool.
|
||||
ErrAlreadyKnown = errors.New("already known")
|
||||
|
||||
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
||||
ErrInvalidSender = errors.New("invalid sender")
|
||||
|
||||
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
||||
// configured for the transaction pool.
|
||||
ErrUnderpriced = errors.New("transaction underpriced")
|
||||
|
||||
// ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced
|
||||
// with a different one without the required price bump.
|
||||
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
||||
|
||||
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||
// maximum allowance of the current block.
|
||||
ErrGasLimit = errors.New("exceeds block gas limit")
|
||||
|
||||
// ErrNegativeValue is a sanity error to ensure no one is able to specify a
|
||||
// transaction with a negative value.
|
||||
ErrNegativeValue = errors.New("negative value")
|
||||
|
||||
// ErrOversizedData is returned if the input data of a transaction is greater
|
||||
// than some meaningful limit a user might use. This is not a consensus error
|
||||
// making the transaction invalid, rather a DOS protection.
|
||||
ErrOversizedData = errors.New("oversized data")
|
||||
|
||||
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
||||
// transaction. Future transactions should only be able to replace other future transactions.
|
||||
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
||||
)
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package txpool
|
||||
package legacypool
|
||||
|
||||
import (
|
||||
"errors"
|
File diff suppressed because it is too large
Load Diff
|
@ -13,7 +13,7 @@
|
|||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
package txpool
|
||||
package legacypool
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
@ -33,7 +33,7 @@ func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gaspric
|
|||
return tx
|
||||
}
|
||||
|
||||
func count(t *testing.T, pool *TxPool) (pending int, queued int) {
|
||||
func count(t *testing.T, pool *LegacyPool) (pending int, queued int) {
|
||||
t.Helper()
|
||||
pending, queued = pool.stats()
|
||||
if err := validatePoolInternals(pool); err != nil {
|
||||
|
@ -42,7 +42,7 @@ func count(t *testing.T, pool *TxPool) (pending int, queued int) {
|
|||
return pending, queued
|
||||
}
|
||||
|
||||
func fillPool(t testing.TB, pool *TxPool) {
|
||||
func fillPool(t testing.TB, pool *LegacyPool) {
|
||||
t.Helper()
|
||||
// Create a number of test accounts, fund them and make transactions
|
||||
executableTxs := types.Transactions{}
|
||||
|
@ -56,8 +56,8 @@ func fillPool(t testing.TB, pool *TxPool) {
|
|||
}
|
||||
}
|
||||
// Import the batch and verify that limits have been enforced
|
||||
pool.AddRemotesSync(executableTxs)
|
||||
pool.AddRemotesSync(nonExecutableTxs)
|
||||
pool.addRemotesSync(executableTxs)
|
||||
pool.addRemotesSync(nonExecutableTxs)
|
||||
pending, queued := pool.Stats()
|
||||
slots := pool.all.Slots()
|
||||
// sanity-check that the test prerequisites are ok (pending full)
|
||||
|
@ -79,12 +79,13 @@ func TestTransactionFutureAttack(t *testing.T) {
|
|||
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed))
|
||||
config := testTxPoolConfig
|
||||
config.GlobalQueue = 100
|
||||
config.GlobalSlots = 100
|
||||
pool := New(config, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
pool := New(config, blockchain)
|
||||
pool.Init(new(big.Int).SetUint64(config.PriceLimit), blockchain.CurrentBlock())
|
||||
defer pool.Close()
|
||||
fillPool(t, pool)
|
||||
pending, _ := pool.Stats()
|
||||
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
|
||||
|
@ -96,7 +97,7 @@ func TestTransactionFutureAttack(t *testing.T) {
|
|||
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key))
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
pool.AddRemotesSync(futureTxs)
|
||||
pool.addRemotesSync(futureTxs)
|
||||
newPending, newQueued := count(t, pool)
|
||||
t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots())
|
||||
}
|
||||
|
@ -115,9 +116,10 @@ func TestTransactionFuture1559(t *testing.T) {
|
|||
t.Parallel()
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
pool := New(testTxPoolConfig, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed))
|
||||
pool := New(testTxPoolConfig, blockchain)
|
||||
pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock())
|
||||
defer pool.Close()
|
||||
|
||||
// Create a number of test accounts, fund them and make transactions
|
||||
fillPool(t, pool)
|
||||
|
@ -131,7 +133,7 @@ func TestTransactionFuture1559(t *testing.T) {
|
|||
for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ {
|
||||
futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key))
|
||||
}
|
||||
pool.AddRemotesSync(futureTxs)
|
||||
pool.addRemotesSync(futureTxs)
|
||||
}
|
||||
newPending, _ := pool.Stats()
|
||||
// Pending should not have been touched
|
||||
|
@ -147,9 +149,10 @@ func TestTransactionZAttack(t *testing.T) {
|
|||
t.Parallel()
|
||||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
pool := New(testTxPoolConfig, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed))
|
||||
pool := New(testTxPoolConfig, blockchain)
|
||||
pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock())
|
||||
defer pool.Close()
|
||||
// Create a number of test accounts, fund them and make transactions
|
||||
fillPool(t, pool)
|
||||
|
||||
|
@ -181,7 +184,7 @@ func TestTransactionZAttack(t *testing.T) {
|
|||
key, _ := crypto.GenerateKey()
|
||||
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), 0x0)
|
||||
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key))
|
||||
pool.AddRemotesSync(futureTxs)
|
||||
pool.addRemotesSync(futureTxs)
|
||||
}
|
||||
|
||||
overDraftTxs := types.Transactions{}
|
||||
|
@ -192,11 +195,11 @@ func TestTransactionZAttack(t *testing.T) {
|
|||
overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key))
|
||||
}
|
||||
}
|
||||
pool.AddRemotesSync(overDraftTxs)
|
||||
pool.AddRemotesSync(overDraftTxs)
|
||||
pool.AddRemotesSync(overDraftTxs)
|
||||
pool.AddRemotesSync(overDraftTxs)
|
||||
pool.AddRemotesSync(overDraftTxs)
|
||||
pool.addRemotesSync(overDraftTxs)
|
||||
pool.addRemotesSync(overDraftTxs)
|
||||
pool.addRemotesSync(overDraftTxs)
|
||||
pool.addRemotesSync(overDraftTxs)
|
||||
pool.addRemotesSync(overDraftTxs)
|
||||
|
||||
newPending, newQueued := count(t, pool)
|
||||
newIvPending := countInvalidPending()
|
||||
|
@ -214,12 +217,13 @@ func TestTransactionZAttack(t *testing.T) {
|
|||
func BenchmarkFutureAttack(b *testing.B) {
|
||||
// Create the pool to test the limit enforcement with
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
blockchain := newTestBlockChain(eip1559Config, 1000000, statedb, new(event.Feed))
|
||||
config := testTxPoolConfig
|
||||
config.GlobalQueue = 100
|
||||
config.GlobalSlots = 100
|
||||
pool := New(config, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
pool := New(config, blockchain)
|
||||
pool.Init(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain.CurrentBlock())
|
||||
defer pool.Close()
|
||||
fillPool(b, pool)
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
@ -231,6 +235,6 @@ func BenchmarkFutureAttack(b *testing.B) {
|
|||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < 5; i++ {
|
||||
pool.AddRemotesSync(futureTxs)
|
||||
pool.addRemotesSync(futureTxs)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package txpool
|
||||
package legacypool
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
|
@ -590,7 +590,7 @@ func (l *pricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool {
|
|||
func (l *pricedList) Discard(slots int, force bool) (types.Transactions, bool) {
|
||||
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
|
||||
for slots > 0 {
|
||||
if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 {
|
||||
if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(&l.urgent).(*types.Transaction)
|
||||
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package txpool
|
||||
package legacypool
|
||||
|
||||
import (
|
||||
"math/big"
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package txpool
|
||||
package legacypool
|
||||
|
||||
import (
|
||||
"sync"
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
// Transaction is a helper struct to group together a canonical transaction with
|
||||
// satellite data items that are needed by the pool but are not part of the chain.
|
||||
type Transaction struct {
|
||||
Tx *types.Transaction // Canonical transaction
|
||||
|
||||
BlobTxBlobs []kzg4844.Blob // Blobs needed by the blob pool
|
||||
BlobTxCommits []kzg4844.Commitment // Commitments needed by the blob pool
|
||||
BlobTxProofs []kzg4844.Proof // Proofs needed by the blob pool
|
||||
}
|
||||
|
||||
// SubPool represents a specialized transaction pool that lives on its own (e.g.
|
||||
// blob pool). Since independent of how many specialized pools we have, they do
|
||||
// need to be updated in lockstep and assemble into one coherent view for block
|
||||
// production, this interface defines the common methods that allow the primary
|
||||
// transaction pool to manage the subpools.
|
||||
type SubPool interface {
|
||||
// Filter is a selector used to decide whether a transaction whould be added
|
||||
// to this particular subpool.
|
||||
Filter(tx *types.Transaction) bool
|
||||
|
||||
// Init sets the base parameters of the subpool, allowing it to load any saved
|
||||
// transactions from disk and also permitting internal maintenance routines to
|
||||
// start up.
|
||||
//
|
||||
// These should not be passed as a constructor argument - nor should the pools
|
||||
// start by themselves - in order to keep multiple subpools in lockstep with
|
||||
// one another.
|
||||
Init(gasTip *big.Int, head *types.Header) error
|
||||
|
||||
// Close terminates any background processing threads and releases any held
|
||||
// resources.
|
||||
Close() error
|
||||
|
||||
// Reset retrieves the current state of the blockchain and ensures the content
|
||||
// of the transaction pool is valid with regard to the chain state.
|
||||
Reset(oldHead, newHead *types.Header)
|
||||
|
||||
// SetGasTip updates the minimum price required by the subpool for a new
|
||||
// transaction, and drops all transactions below this threshold.
|
||||
SetGasTip(tip *big.Int)
|
||||
|
||||
// Has returns an indicator whether subpool has a transaction cached with the
|
||||
// given hash.
|
||||
Has(hash common.Hash) bool
|
||||
|
||||
// Get returns a transaction if it is contained in the pool, or nil otherwise.
|
||||
Get(hash common.Hash) *Transaction
|
||||
|
||||
// Add enqueues a batch of transactions into the pool if they are valid. Due
|
||||
// to the large transaction churn, add may postpone fully integrating the tx
|
||||
// to a later point to batch multiple ones together.
|
||||
Add(txs []*Transaction, local bool, sync bool) []error
|
||||
|
||||
// Pending retrieves all currently processable transactions, grouped by origin
|
||||
// account and sorted by nonce.
|
||||
Pending(enforceTips bool) map[common.Address][]*types.Transaction
|
||||
|
||||
// SubscribeTransactions subscribes to new transaction events.
|
||||
SubscribeTransactions(ch chan<- core.NewTxsEvent) event.Subscription
|
||||
|
||||
// Nonce returns the next nonce of an account, with all transactions executable
|
||||
// by the pool already applied on top.
|
||||
Nonce(addr common.Address) uint64
|
||||
|
||||
// Stats retrieves the current pool stats, namely the number of pending and the
|
||||
// number of queued (non-executable) transactions.
|
||||
Stats() (int, int)
|
||||
|
||||
// Content retrieves the data content of the transaction pool, returning all the
|
||||
// pending as well as queued transactions, grouped by account and sorted by nonce.
|
||||
Content() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction)
|
||||
|
||||
// ContentFrom retrieves the data content of the transaction pool, returning the
|
||||
// pending as well as queued transactions of this address, grouped by nonce.
|
||||
ContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction)
|
||||
|
||||
// Locals retrieves the accounts currently considered local by the pool.
|
||||
Locals() []common.Address
|
||||
|
||||
// Status returns the known status (unknown/pending/queued) of a transaction
|
||||
// identified by their hashes.
|
||||
Status(hash common.Hash) TxStatus
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -18,7 +18,6 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"hash"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
@ -26,9 +25,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/blocktest"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// from bcValidBlockTest.json, "SimpleTx"
|
||||
|
@ -217,31 +216,6 @@ func BenchmarkEncodeBlock(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
// testHasher is the helper tool for transaction/receipt list hashing.
|
||||
// The original hasher is trie, in order to get rid of import cycle,
|
||||
// use the testing hasher instead.
|
||||
type testHasher struct {
|
||||
hasher hash.Hash
|
||||
}
|
||||
|
||||
func newHasher() *testHasher {
|
||||
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
|
||||
}
|
||||
|
||||
func (h *testHasher) Reset() {
|
||||
h.hasher.Reset()
|
||||
}
|
||||
|
||||
func (h *testHasher) Update(key, val []byte) error {
|
||||
h.hasher.Write(key)
|
||||
h.hasher.Write(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *testHasher) Hash() common.Hash {
|
||||
return common.BytesToHash(h.hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func makeBenchBlock() *Block {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
|
@ -280,7 +254,7 @@ func makeBenchBlock() *Block {
|
|||
Extra: []byte("benchmark uncle"),
|
||||
}
|
||||
}
|
||||
return NewBlock(header, txs, uncles, receipts, newHasher())
|
||||
return NewBlock(header, txs, uncles, receipts, blocktest.NewHasher())
|
||||
}
|
||||
|
||||
func TestRlpDecodeParentHash(t *testing.T) {
|
||||
|
|
|
@ -35,6 +35,29 @@ type StateAccount struct {
|
|||
CodeHash []byte
|
||||
}
|
||||
|
||||
// NewEmptyStateAccount constructs an empty state account.
|
||||
func NewEmptyStateAccount() *StateAccount {
|
||||
return &StateAccount{
|
||||
Balance: new(big.Int),
|
||||
Root: EmptyRootHash,
|
||||
CodeHash: EmptyCodeHash.Bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy returns a deep-copied state account object.
|
||||
func (acct *StateAccount) Copy() *StateAccount {
|
||||
var balance *big.Int
|
||||
if acct.Balance != nil {
|
||||
balance = new(big.Int).Set(acct.Balance)
|
||||
}
|
||||
return &StateAccount{
|
||||
Nonce: acct.Nonce,
|
||||
Balance: balance,
|
||||
Root: acct.Root,
|
||||
CodeHash: common.CopyBytes(acct.CodeHash),
|
||||
}
|
||||
}
|
||||
|
||||
// SlimAccount is a modified version of an Account, where the root is replaced
|
||||
// with a byte slice. This format can be used to represent full-consensus format
|
||||
// or slim format which replaces the empty root and code hash as nil byte slice.
|
||||
|
|
|
@ -555,10 +555,10 @@ func (s *TxByPriceAndTime) Pop() interface{} {
|
|||
// transactions in a profit-maximizing sorted order, while supporting removing
|
||||
// entire batches of transactions for non-executable accounts.
|
||||
type TransactionsByPriceAndNonce struct {
|
||||
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
|
||||
heads TxByPriceAndTime // Next transaction for each unique account (price heap)
|
||||
signer Signer // Signer for the set of transactions
|
||||
baseFee *big.Int // Current base fee
|
||||
txs map[common.Address][]*Transaction // Per account nonce-sorted list of transactions
|
||||
heads TxByPriceAndTime // Next transaction for each unique account (price heap)
|
||||
signer Signer // Signer for the set of transactions
|
||||
baseFee *big.Int // Current base fee
|
||||
}
|
||||
|
||||
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
|
||||
|
@ -566,7 +566,7 @@ type TransactionsByPriceAndNonce struct {
|
|||
//
|
||||
// Note, the input map is reowned so the caller should not interact any more with
|
||||
// if after providing it to the constructor.
|
||||
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *big.Int) *TransactionsByPriceAndNonce {
|
||||
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address][]*Transaction, baseFee *big.Int) *TransactionsByPriceAndNonce {
|
||||
// Initialize a price and received time based heap with the head transactions
|
||||
heads := make(TxByPriceAndTime, 0, len(txs))
|
||||
for from, accTxs := range txs {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue