mirror of https://github.com/ethereum/go-ethereum
Merge pull request #30456 from ethereum/master
Merge branch 'master' into release/1.14release/1.14 v1.14.9
commit
c350d3acd5
@ -1,113 +0,0 @@ |
|||||||
// Copyright 2021 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 core |
|
||||||
|
|
||||||
import ( |
|
||||||
crand "crypto/rand" |
|
||||||
"errors" |
|
||||||
"math/big" |
|
||||||
mrand "math/rand" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/common/math" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/log" |
|
||||||
"github.com/ethereum/go-ethereum/params" |
|
||||||
) |
|
||||||
|
|
||||||
// ChainReader defines a small collection of methods needed to access the local
|
|
||||||
// blockchain during header verification. It's implemented by both blockchain
|
|
||||||
// and lightchain.
|
|
||||||
type ChainReader interface { |
|
||||||
// Config retrieves the header chain's chain configuration.
|
|
||||||
Config() *params.ChainConfig |
|
||||||
|
|
||||||
// GetTd returns the total difficulty of a local block.
|
|
||||||
GetTd(common.Hash, uint64) *big.Int |
|
||||||
} |
|
||||||
|
|
||||||
// ForkChoice is the fork chooser based on the highest total difficulty of the
|
|
||||||
// chain(the fork choice used in the eth1) and the external fork choice (the fork
|
|
||||||
// choice used in the eth2). This main goal of this ForkChoice is not only for
|
|
||||||
// offering fork choice during the eth1/2 merge phase, but also keep the compatibility
|
|
||||||
// for all other proof-of-work networks.
|
|
||||||
type ForkChoice struct { |
|
||||||
chain ChainReader |
|
||||||
rand *mrand.Rand |
|
||||||
|
|
||||||
// preserve is a helper function used in td fork choice.
|
|
||||||
// Miners will prefer to choose the local mined block if the
|
|
||||||
// local td is equal to the extern one. It can be nil for light
|
|
||||||
// client
|
|
||||||
preserve func(header *types.Header) bool |
|
||||||
} |
|
||||||
|
|
||||||
func NewForkChoice(chainReader ChainReader, preserve func(header *types.Header) bool) *ForkChoice { |
|
||||||
// Seed a fast but crypto originating random generator
|
|
||||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) |
|
||||||
if err != nil { |
|
||||||
log.Crit("Failed to initialize random seed", "err", err) |
|
||||||
} |
|
||||||
return &ForkChoice{ |
|
||||||
chain: chainReader, |
|
||||||
rand: mrand.New(mrand.NewSource(seed.Int64())), |
|
||||||
preserve: preserve, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// ReorgNeeded returns whether the reorg should be applied
|
|
||||||
// based on the given external header and local canonical chain.
|
|
||||||
// In the td mode, the new head is chosen if the corresponding
|
|
||||||
// total difficulty is higher. In the extern mode, the trusted
|
|
||||||
// header is always selected as the head.
|
|
||||||
func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (bool, error) { |
|
||||||
var ( |
|
||||||
localTD = f.chain.GetTd(current.Hash(), current.Number.Uint64()) |
|
||||||
externTd = f.chain.GetTd(extern.Hash(), extern.Number.Uint64()) |
|
||||||
) |
|
||||||
if localTD == nil || externTd == nil { |
|
||||||
return false, errors.New("missing td") |
|
||||||
} |
|
||||||
// Accept the new header as the chain head if the transition
|
|
||||||
// is already triggered. We assume all the headers after the
|
|
||||||
// transition come from the trusted consensus layer.
|
|
||||||
if ttd := f.chain.Config().TerminalTotalDifficulty; ttd != nil && ttd.Cmp(externTd) <= 0 { |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
|
||||||
if diff := externTd.Cmp(localTD); diff > 0 { |
|
||||||
return true, nil |
|
||||||
} else if diff < 0 { |
|
||||||
return false, nil |
|
||||||
} |
|
||||||
// Local and external difficulty is identical.
|
|
||||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
|
||||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
|
||||||
reorg := false |
|
||||||
externNum, localNum := extern.Number.Uint64(), current.Number.Uint64() |
|
||||||
if externNum < localNum { |
|
||||||
reorg = true |
|
||||||
} else if externNum == localNum { |
|
||||||
var currentPreserve, externPreserve bool |
|
||||||
if f.preserve != nil { |
|
||||||
currentPreserve, externPreserve = f.preserve(current), f.preserve(extern) |
|
||||||
} |
|
||||||
reorg = !currentPreserve && (externPreserve || f.rand.Float64() < 0.5) |
|
||||||
} |
|
||||||
return reorg, nil |
|
||||||
} |
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,313 @@ |
|||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"maps" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/state/snapshot" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
"github.com/ethereum/go-ethereum/trie" |
||||||
|
"github.com/ethereum/go-ethereum/trie/utils" |
||||||
|
"github.com/ethereum/go-ethereum/triedb" |
||||||
|
) |
||||||
|
|
||||||
|
// Reader defines the interface for accessing accounts and storage slots
|
||||||
|
// associated with a specific state.
|
||||||
|
type Reader interface { |
||||||
|
// Account retrieves the account associated with a particular address.
|
||||||
|
//
|
||||||
|
// - Returns a nil account if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned account is safe to modify after the call
|
||||||
|
Account(addr common.Address) (*types.StateAccount, error) |
||||||
|
|
||||||
|
// Storage retrieves the storage slot associated with a particular account
|
||||||
|
// address and slot key.
|
||||||
|
//
|
||||||
|
// - Returns an empty slot if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned storage slot is safe to modify after the call
|
||||||
|
Storage(addr common.Address, slot common.Hash) (common.Hash, error) |
||||||
|
|
||||||
|
// Copy returns a deep-copied state reader.
|
||||||
|
Copy() Reader |
||||||
|
} |
||||||
|
|
||||||
|
// stateReader is a wrapper over the state snapshot and implements the Reader
|
||||||
|
// interface. It provides an efficient way to access flat state.
|
||||||
|
type stateReader struct { |
||||||
|
snap snapshot.Snapshot |
||||||
|
buff crypto.KeccakState |
||||||
|
} |
||||||
|
|
||||||
|
// newStateReader constructs a flat state reader with on the specified state root.
|
||||||
|
func newStateReader(root common.Hash, snaps *snapshot.Tree) (*stateReader, error) { |
||||||
|
snap := snaps.Snapshot(root) |
||||||
|
if snap == nil { |
||||||
|
return nil, errors.New("snapshot is not available") |
||||||
|
} |
||||||
|
return &stateReader{ |
||||||
|
snap: snap, |
||||||
|
buff: crypto.NewKeccakState(), |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Account implements Reader, retrieving the account specified by the address.
|
||||||
|
//
|
||||||
|
// An error will be returned if the associated snapshot is already stale or
|
||||||
|
// the requested account is not yet covered by the snapshot.
|
||||||
|
//
|
||||||
|
// The returned account might be nil if it's not existent.
|
||||||
|
func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) { |
||||||
|
ret, err := r.snap.Account(crypto.HashData(r.buff, addr.Bytes())) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if ret == nil { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
acct := &types.StateAccount{ |
||||||
|
Nonce: ret.Nonce, |
||||||
|
Balance: ret.Balance, |
||||||
|
CodeHash: ret.CodeHash, |
||||||
|
Root: common.BytesToHash(ret.Root), |
||||||
|
} |
||||||
|
if len(acct.CodeHash) == 0 { |
||||||
|
acct.CodeHash = types.EmptyCodeHash.Bytes() |
||||||
|
} |
||||||
|
if acct.Root == (common.Hash{}) { |
||||||
|
acct.Root = types.EmptyRootHash |
||||||
|
} |
||||||
|
return acct, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Storage implements Reader, retrieving the storage slot specified by the
|
||||||
|
// address and slot key.
|
||||||
|
//
|
||||||
|
// An error will be returned if the associated snapshot is already stale or
|
||||||
|
// the requested storage slot is not yet covered by the snapshot.
|
||||||
|
//
|
||||||
|
// The returned storage slot might be empty if it's not existent.
|
||||||
|
func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { |
||||||
|
addrHash := crypto.HashData(r.buff, addr.Bytes()) |
||||||
|
slotHash := crypto.HashData(r.buff, key.Bytes()) |
||||||
|
ret, err := r.snap.Storage(addrHash, slotHash) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
if len(ret) == 0 { |
||||||
|
return common.Hash{}, nil |
||||||
|
} |
||||||
|
// Perform the rlp-decode as the slot value is RLP-encoded in the state
|
||||||
|
// snapshot.
|
||||||
|
_, content, _, err := rlp.Split(ret) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
var value common.Hash |
||||||
|
value.SetBytes(content) |
||||||
|
return value, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Copy implements Reader, returning a deep-copied snap reader.
|
||||||
|
func (r *stateReader) Copy() Reader { |
||||||
|
return &stateReader{ |
||||||
|
snap: r.snap, |
||||||
|
buff: crypto.NewKeccakState(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// trieReader implements the Reader interface, providing functions to access
|
||||||
|
// state from the referenced trie.
|
||||||
|
type trieReader struct { |
||||||
|
root common.Hash // State root which uniquely represent a state
|
||||||
|
db *triedb.Database // Database for loading trie
|
||||||
|
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||||
|
mainTrie Trie // Main trie, resolved in constructor
|
||||||
|
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
||||||
|
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
||||||
|
} |
||||||
|
|
||||||
|
// trieReader constructs a trie reader of the specific state. An error will be
|
||||||
|
// returned if the associated trie specified by root is not existent.
|
||||||
|
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) { |
||||||
|
var ( |
||||||
|
tr Trie |
||||||
|
err error |
||||||
|
) |
||||||
|
if !db.IsVerkle() { |
||||||
|
tr, err = trie.NewStateTrie(trie.StateTrieID(root), db) |
||||||
|
} else { |
||||||
|
tr, err = trie.NewVerkleTrie(root, db, cache) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &trieReader{ |
||||||
|
root: root, |
||||||
|
db: db, |
||||||
|
buff: crypto.NewKeccakState(), |
||||||
|
mainTrie: tr, |
||||||
|
subRoots: make(map[common.Address]common.Hash), |
||||||
|
subTries: make(map[common.Address]Trie), |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Account implements Reader, retrieving the account specified by the address.
|
||||||
|
//
|
||||||
|
// An error will be returned if the trie state is corrupted. An nil account
|
||||||
|
// will be returned if it's not existent in the trie.
|
||||||
|
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) { |
||||||
|
account, err := r.mainTrie.GetAccount(addr) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if account == nil { |
||||||
|
r.subRoots[addr] = types.EmptyRootHash |
||||||
|
} else { |
||||||
|
r.subRoots[addr] = account.Root |
||||||
|
} |
||||||
|
return account, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Storage implements Reader, retrieving the storage slot specified by the
|
||||||
|
// address and slot key.
|
||||||
|
//
|
||||||
|
// An error will be returned if the trie state is corrupted. An empty storage
|
||||||
|
// slot will be returned if it's not existent in the trie.
|
||||||
|
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { |
||||||
|
var ( |
||||||
|
tr Trie |
||||||
|
found bool |
||||||
|
value common.Hash |
||||||
|
) |
||||||
|
if r.db.IsVerkle() { |
||||||
|
tr = r.mainTrie |
||||||
|
} else { |
||||||
|
tr, found = r.subTries[addr] |
||||||
|
if !found { |
||||||
|
root, ok := r.subRoots[addr] |
||||||
|
|
||||||
|
// The storage slot is accessed without account caching. It's unexpected
|
||||||
|
// behavior but try to resolve the account first anyway.
|
||||||
|
if !ok { |
||||||
|
_, err := r.Account(addr) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
root = r.subRoots[addr] |
||||||
|
} |
||||||
|
var err error |
||||||
|
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.HashData(r.buff, addr.Bytes()), root), r.db) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
r.subTries[addr] = tr |
||||||
|
} |
||||||
|
} |
||||||
|
ret, err := tr.GetStorage(addr, key.Bytes()) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
value.SetBytes(ret) |
||||||
|
return value, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Copy implements Reader, returning a deep-copied trie reader.
|
||||||
|
func (r *trieReader) Copy() Reader { |
||||||
|
tries := make(map[common.Address]Trie) |
||||||
|
for addr, tr := range r.subTries { |
||||||
|
tries[addr] = mustCopyTrie(tr) |
||||||
|
} |
||||||
|
return &trieReader{ |
||||||
|
root: r.root, |
||||||
|
db: r.db, |
||||||
|
buff: crypto.NewKeccakState(), |
||||||
|
mainTrie: mustCopyTrie(r.mainTrie), |
||||||
|
subRoots: maps.Clone(r.subRoots), |
||||||
|
subTries: tries, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// multiReader is the aggregation of a list of Reader interface, providing state
|
||||||
|
// access by leveraging all readers. The checking priority is determined by the
|
||||||
|
// position in the reader list.
|
||||||
|
type multiReader struct { |
||||||
|
readers []Reader // List of readers, sorted by checking priority
|
||||||
|
} |
||||||
|
|
||||||
|
// newMultiReader constructs a multiReader instance with the given readers. The
|
||||||
|
// priority among readers is assumed to be sorted. Note, it must contain at least
|
||||||
|
// one reader for constructing a multiReader.
|
||||||
|
func newMultiReader(readers ...Reader) (*multiReader, error) { |
||||||
|
if len(readers) == 0 { |
||||||
|
return nil, errors.New("empty reader set") |
||||||
|
} |
||||||
|
return &multiReader{ |
||||||
|
readers: readers, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Account implementing Reader interface, retrieving the account associated with
|
||||||
|
// a particular address.
|
||||||
|
//
|
||||||
|
// - Returns a nil account if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned account is safe to modify after the call
|
||||||
|
func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) { |
||||||
|
var errs []error |
||||||
|
for _, reader := range r.readers { |
||||||
|
acct, err := reader.Account(addr) |
||||||
|
if err == nil { |
||||||
|
return acct, nil |
||||||
|
} |
||||||
|
errs = append(errs, err) |
||||||
|
} |
||||||
|
return nil, errors.Join(errs...) |
||||||
|
} |
||||||
|
|
||||||
|
// Storage implementing Reader interface, retrieving the storage slot associated
|
||||||
|
// with a particular account address and slot key.
|
||||||
|
//
|
||||||
|
// - Returns an empty slot if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned storage slot is safe to modify after the call
|
||||||
|
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { |
||||||
|
var errs []error |
||||||
|
for _, reader := range r.readers { |
||||||
|
slot, err := reader.Storage(addr, slot) |
||||||
|
if err == nil { |
||||||
|
return slot, nil |
||||||
|
} |
||||||
|
errs = append(errs, err) |
||||||
|
} |
||||||
|
return common.Hash{}, errors.Join(errs...) |
||||||
|
} |
||||||
|
|
||||||
|
// Copy implementing Reader interface, returning a deep-copied state reader.
|
||||||
|
func (r *multiReader) Copy() Reader { |
||||||
|
var readers []Reader |
||||||
|
for _, reader := range r.readers { |
||||||
|
readers = append(readers, reader.Copy()) |
||||||
|
} |
||||||
|
return &multiReader{readers: readers} |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type Deposit -field-override depositMarshaling -out gen_deposit_json.go
|
||||||
|
|
||||||
|
// Deposit contains EIP-6110 deposit data.
|
||||||
|
type Deposit struct { |
||||||
|
PublicKey [48]byte `json:"pubkey"` // public key of validator
|
||||||
|
WithdrawalCredentials common.Hash `json:"withdrawalCredentials"` // beneficiary of the validator funds
|
||||||
|
Amount uint64 `json:"amount"` // deposit size in Gwei
|
||||||
|
Signature [96]byte `json:"signature"` // signature over deposit msg
|
||||||
|
Index uint64 `json:"index"` // deposit count value
|
||||||
|
} |
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type depositMarshaling struct { |
||||||
|
PublicKey hexutil.Bytes |
||||||
|
WithdrawalCredentials hexutil.Bytes |
||||||
|
Amount hexutil.Uint64 |
||||||
|
Signature hexutil.Bytes |
||||||
|
Index hexutil.Uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// Deposits implements DerivableList for requests.
|
||||||
|
type Deposits []*Deposit |
||||||
|
|
||||||
|
// Len returns the length of s.
|
||||||
|
func (s Deposits) Len() int { return len(s) } |
||||||
|
|
||||||
|
// EncodeIndex encodes the i'th deposit to s.
|
||||||
|
func (s Deposits) EncodeIndex(i int, w *bytes.Buffer) { |
||||||
|
rlp.Encode(w, s[i]) |
||||||
|
} |
||||||
|
|
||||||
|
// UnpackIntoDeposit unpacks a serialized DepositEvent.
|
||||||
|
func UnpackIntoDeposit(data []byte) (*Deposit, error) { |
||||||
|
if len(data) != 576 { |
||||||
|
return nil, fmt.Errorf("deposit wrong length: want 576, have %d", len(data)) |
||||||
|
} |
||||||
|
var d Deposit |
||||||
|
// The ABI encodes the position of dynamic elements first. Since there are 5
|
||||||
|
// elements, skip over the positional data. The first 32 bytes of dynamic
|
||||||
|
// elements also encode their actual length. Skip over that value too.
|
||||||
|
b := 32*5 + 32 |
||||||
|
// PublicKey is the first element. ABI encoding pads values to 32 bytes, so
|
||||||
|
// despite BLS public keys being length 48, the value length here is 64. Then
|
||||||
|
// skip over the next length value.
|
||||||
|
copy(d.PublicKey[:], data[b:b+48]) |
||||||
|
b += 48 + 16 + 32 |
||||||
|
// WithdrawalCredentials is 32 bytes. Read that value then skip over next
|
||||||
|
// length.
|
||||||
|
copy(d.WithdrawalCredentials[:], data[b:b+32]) |
||||||
|
b += 32 + 32 |
||||||
|
// Amount is 8 bytes, but it is padded to 32. Skip over it and the next
|
||||||
|
// length.
|
||||||
|
d.Amount = binary.LittleEndian.Uint64(data[b : b+8]) |
||||||
|
b += 8 + 24 + 32 |
||||||
|
// Signature is 96 bytes. Skip over it and the next length.
|
||||||
|
copy(d.Signature[:], data[b:b+96]) |
||||||
|
b += 96 + 32 |
||||||
|
// Amount is 8 bytes.
|
||||||
|
d.Index = binary.LittleEndian.Uint64(data[b : b+8]) |
||||||
|
|
||||||
|
return &d, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d *Deposit) requestType() byte { return DepositRequestType } |
||||||
|
func (d *Deposit) encode(b *bytes.Buffer) error { return rlp.Encode(b, d) } |
||||||
|
func (d *Deposit) decode(input []byte) error { return rlp.DecodeBytes(input, d) } |
||||||
|
func (d *Deposit) copy() RequestData { |
||||||
|
return &Deposit{ |
||||||
|
PublicKey: d.PublicKey, |
||||||
|
WithdrawalCredentials: d.WithdrawalCredentials, |
||||||
|
Amount: d.Amount, |
||||||
|
Signature: d.Signature, |
||||||
|
Index: d.Index, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"reflect" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
depositABI = abi.ABI{Methods: map[string]abi.Method{"DepositEvent": depositEvent}} |
||||||
|
bytesT, _ = abi.NewType("bytes", "", nil) |
||||||
|
depositEvent = abi.NewMethod("DepositEvent", "DepositEvent", abi.Function, "", false, false, []abi.Argument{ |
||||||
|
{Name: "pubkey", Type: bytesT, Indexed: false}, |
||||||
|
{Name: "withdrawal_credentials", Type: bytesT, Indexed: false}, |
||||||
|
{Name: "amount", Type: bytesT, Indexed: false}, |
||||||
|
{Name: "signature", Type: bytesT, Indexed: false}, |
||||||
|
{Name: "index", Type: bytesT, Indexed: false}}, nil, |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
// FuzzUnpackIntoDeposit tries roundtrip packing and unpacking of deposit events.
|
||||||
|
func FuzzUnpackIntoDeposit(f *testing.F) { |
||||||
|
for _, tt := range []struct { |
||||||
|
pubkey string |
||||||
|
wxCred string |
||||||
|
amount string |
||||||
|
sig string |
||||||
|
index string |
||||||
|
}{ |
||||||
|
{ |
||||||
|
pubkey: "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", |
||||||
|
wxCred: "2222222222222222222222222222222222222222222222222222222222222222", |
||||||
|
amount: "3333333333333333", |
||||||
|
sig: "444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444", |
||||||
|
index: "5555555555555555", |
||||||
|
}, |
||||||
|
} { |
||||||
|
f.Add(common.FromHex(tt.pubkey), common.FromHex(tt.wxCred), common.FromHex(tt.amount), common.FromHex(tt.sig), common.FromHex(tt.index)) |
||||||
|
} |
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, p []byte, w []byte, a []byte, s []byte, i []byte) { |
||||||
|
var ( |
||||||
|
pubkey [48]byte |
||||||
|
wxCred [32]byte |
||||||
|
amount [8]byte |
||||||
|
sig [96]byte |
||||||
|
index [8]byte |
||||||
|
) |
||||||
|
copy(pubkey[:], p) |
||||||
|
copy(wxCred[:], w) |
||||||
|
copy(amount[:], a) |
||||||
|
copy(sig[:], s) |
||||||
|
copy(index[:], i) |
||||||
|
|
||||||
|
want := Deposit{ |
||||||
|
PublicKey: pubkey, |
||||||
|
WithdrawalCredentials: wxCred, |
||||||
|
Amount: binary.LittleEndian.Uint64(amount[:]), |
||||||
|
Signature: sig, |
||||||
|
Index: binary.LittleEndian.Uint64(index[:]), |
||||||
|
} |
||||||
|
out, err := depositABI.Pack("DepositEvent", want.PublicKey[:], want.WithdrawalCredentials[:], amount[:], want.Signature[:], index[:]) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("error packing deposit: %v", err) |
||||||
|
} |
||||||
|
got, err := UnpackIntoDeposit(out[4:]) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("error unpacking deposit: %v", err) |
||||||
|
} |
||||||
|
if !reflect.DeepEqual(want, *got) { |
||||||
|
t.Errorf("roundtrip failed: want %v, got %v", want, got) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
) |
||||||
|
|
||||||
|
var _ = (*depositMarshaling)(nil) |
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (d Deposit) MarshalJSON() ([]byte, error) { |
||||||
|
type Deposit struct { |
||||||
|
PublicKey hexutil.Bytes `json:"pubkey"` |
||||||
|
WithdrawalCredentials hexutil.Bytes `json:"withdrawalCredentials"` |
||||||
|
Amount hexutil.Uint64 `json:"amount"` |
||||||
|
Signature hexutil.Bytes `json:"signature"` |
||||||
|
Index hexutil.Uint64 `json:"index"` |
||||||
|
} |
||||||
|
var enc Deposit |
||||||
|
enc.PublicKey = d.PublicKey[:] |
||||||
|
enc.WithdrawalCredentials = d.WithdrawalCredentials[:] |
||||||
|
enc.Amount = hexutil.Uint64(d.Amount) |
||||||
|
enc.Signature = d.Signature[:] |
||||||
|
enc.Index = hexutil.Uint64(d.Index) |
||||||
|
return json.Marshal(&enc) |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (d *Deposit) UnmarshalJSON(input []byte) error { |
||||||
|
type Deposit struct { |
||||||
|
PublicKey *hexutil.Bytes `json:"pubkey"` |
||||||
|
WithdrawalCredentials *hexutil.Bytes `json:"withdrawalCredentials"` |
||||||
|
Amount *hexutil.Uint64 `json:"amount"` |
||||||
|
Signature *hexutil.Bytes `json:"signature"` |
||||||
|
Index *hexutil.Uint64 `json:"index"` |
||||||
|
} |
||||||
|
var dec Deposit |
||||||
|
if err := json.Unmarshal(input, &dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if dec.PublicKey != nil { |
||||||
|
if len(*dec.PublicKey) != len(d.PublicKey) { |
||||||
|
return errors.New("field 'pubkey' has wrong length, need 48 items") |
||||||
|
} |
||||||
|
copy(d.PublicKey[:], *dec.PublicKey) |
||||||
|
} |
||||||
|
if dec.WithdrawalCredentials != nil { |
||||||
|
if len(*dec.WithdrawalCredentials) != len(d.WithdrawalCredentials) { |
||||||
|
return errors.New("field 'withdrawalCredentials' has wrong length, need 32 items") |
||||||
|
} |
||||||
|
copy(d.WithdrawalCredentials[:], *dec.WithdrawalCredentials) |
||||||
|
} |
||||||
|
if dec.Amount != nil { |
||||||
|
d.Amount = uint64(*dec.Amount) |
||||||
|
} |
||||||
|
if dec.Signature != nil { |
||||||
|
if len(*dec.Signature) != len(d.Signature) { |
||||||
|
return errors.New("field 'signature' has wrong length, need 96 items") |
||||||
|
} |
||||||
|
copy(d.Signature[:], *dec.Signature) |
||||||
|
} |
||||||
|
if dec.Index != nil { |
||||||
|
d.Index = uint64(*dec.Index) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue