mirror of https://github.com/ethereum/go-ethereum
commit
838fc25dda
@ -0,0 +1,200 @@ |
||||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"encoding/hex" |
||||
"encoding/json" |
||||
"fmt" |
||||
"io/fs" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/urfave/cli/v2" |
||||
) |
||||
|
||||
func init() { |
||||
jt = vm.NewPragueEOFInstructionSetForTesting() |
||||
} |
||||
|
||||
var ( |
||||
jt vm.JumpTable |
||||
initcode = "INITCODE" |
||||
) |
||||
|
||||
func eofParseAction(ctx *cli.Context) error { |
||||
// If `--test` is set, parse and validate the reference test at the provided path.
|
||||
if ctx.IsSet(refTestFlag.Name) { |
||||
var ( |
||||
file = ctx.String(refTestFlag.Name) |
||||
executedTests int |
||||
passedTests int |
||||
) |
||||
err := filepath.Walk(file, func(path string, info fs.FileInfo, err error) error { |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if info.IsDir() { |
||||
return nil |
||||
} |
||||
log.Debug("Executing test", "name", info.Name()) |
||||
passed, tot, err := executeTest(path) |
||||
passedTests += passed |
||||
executedTests += tot |
||||
return err |
||||
}) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
log.Info("Executed tests", "passed", passedTests, "total executed", executedTests) |
||||
return nil |
||||
} |
||||
// If `--hex` is set, parse and validate the hex string argument.
|
||||
if ctx.IsSet(hexFlag.Name) { |
||||
if _, err := parseAndValidate(ctx.String(hexFlag.Name), false); err != nil { |
||||
return fmt.Errorf("err: %w", err) |
||||
} |
||||
fmt.Println("OK") |
||||
return nil |
||||
} |
||||
// If neither are passed in, read input from stdin.
|
||||
scanner := bufio.NewScanner(os.Stdin) |
||||
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024) |
||||
for scanner.Scan() { |
||||
l := strings.TrimSpace(scanner.Text()) |
||||
if strings.HasPrefix(l, "#") || l == "" { |
||||
continue |
||||
} |
||||
if _, err := parseAndValidate(l, false); err != nil { |
||||
fmt.Printf("err: %v\n", err) |
||||
} else { |
||||
fmt.Println("OK") |
||||
} |
||||
} |
||||
if err := scanner.Err(); err != nil { |
||||
fmt.Println(err.Error()) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
type refTests struct { |
||||
Vectors map[string]eOFTest `json:"vectors"` |
||||
} |
||||
|
||||
type eOFTest struct { |
||||
Code string `json:"code"` |
||||
Results map[string]etResult `json:"results"` |
||||
ContainerKind string `json:"containerKind"` |
||||
} |
||||
|
||||
type etResult struct { |
||||
Result bool `json:"result"` |
||||
Exception string `json:"exception,omitempty"` |
||||
} |
||||
|
||||
func executeTest(path string) (int, int, error) { |
||||
src, err := os.ReadFile(path) |
||||
if err != nil { |
||||
return 0, 0, err |
||||
} |
||||
var testsByName map[string]refTests |
||||
if err := json.Unmarshal(src, &testsByName); err != nil { |
||||
return 0, 0, err |
||||
} |
||||
passed, total := 0, 0 |
||||
for testsName, tests := range testsByName { |
||||
for name, tt := range tests.Vectors { |
||||
for fork, r := range tt.Results { |
||||
total++ |
||||
_, err := parseAndValidate(tt.Code, tt.ContainerKind == initcode) |
||||
if r.Result && err != nil { |
||||
log.Error("Test failure, expected validation success", "name", testsName, "idx", name, "fork", fork, "err", err) |
||||
continue |
||||
} |
||||
if !r.Result && err == nil { |
||||
log.Error("Test failure, expected validation error", "name", testsName, "idx", name, "fork", fork, "have err", r.Exception, "err", err) |
||||
continue |
||||
} |
||||
passed++ |
||||
} |
||||
} |
||||
} |
||||
return passed, total, nil |
||||
} |
||||
|
||||
func parseAndValidate(s string, isInitCode bool) (*vm.Container, error) { |
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") { |
||||
s = s[2:] |
||||
} |
||||
b, err := hex.DecodeString(s) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("unable to decode data: %w", err) |
||||
} |
||||
return parse(b, isInitCode) |
||||
} |
||||
|
||||
func parse(b []byte, isInitCode bool) (*vm.Container, error) { |
||||
var c vm.Container |
||||
if err := c.UnmarshalBinary(b, isInitCode); err != nil { |
||||
return nil, err |
||||
} |
||||
if err := c.ValidateCode(&jt, isInitCode); err != nil { |
||||
return nil, err |
||||
} |
||||
return &c, nil |
||||
} |
||||
|
||||
func eofDumpAction(ctx *cli.Context) error { |
||||
// If `--hex` is set, parse and validate the hex string argument.
|
||||
if ctx.IsSet(hexFlag.Name) { |
||||
return eofDump(ctx.String(hexFlag.Name)) |
||||
} |
||||
// Otherwise read from stdin
|
||||
scanner := bufio.NewScanner(os.Stdin) |
||||
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024) |
||||
for scanner.Scan() { |
||||
l := strings.TrimSpace(scanner.Text()) |
||||
if strings.HasPrefix(l, "#") || l == "" { |
||||
continue |
||||
} |
||||
if err := eofDump(l); err != nil { |
||||
return err |
||||
} |
||||
fmt.Println("") |
||||
} |
||||
return scanner.Err() |
||||
} |
||||
|
||||
func eofDump(hexdata string) error { |
||||
if len(hexdata) >= 2 && strings.HasPrefix(hexdata, "0x") { |
||||
hexdata = hexdata[2:] |
||||
} |
||||
b, err := hex.DecodeString(hexdata) |
||||
if err != nil { |
||||
return fmt.Errorf("unable to decode data: %w", err) |
||||
} |
||||
var c vm.Container |
||||
if err := c.UnmarshalBinary(b, false); err != nil { |
||||
return err |
||||
} |
||||
fmt.Println(c.String()) |
||||
return nil |
||||
} |
@ -0,0 +1,166 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"bytes" |
||||
"encoding/hex" |
||||
"fmt" |
||||
"os" |
||||
"strings" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/vm" |
||||
) |
||||
|
||||
func FuzzEofParsing(f *testing.F) { |
||||
// Seed with corpus from execution-spec-tests
|
||||
for i := 0; ; i++ { |
||||
fname := fmt.Sprintf("testdata/eof/eof_corpus_%d.txt", i) |
||||
corpus, err := os.Open(fname) |
||||
if err != nil { |
||||
break |
||||
} |
||||
f.Logf("Reading seed data from %v", fname) |
||||
scanner := bufio.NewScanner(corpus) |
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024) |
||||
for scanner.Scan() { |
||||
s := scanner.Text() |
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") { |
||||
s = s[2:] |
||||
} |
||||
b, err := hex.DecodeString(s) |
||||
if err != nil { |
||||
panic(err) // rotten corpus
|
||||
} |
||||
f.Add(b) |
||||
} |
||||
corpus.Close() |
||||
if err := scanner.Err(); err != nil { |
||||
panic(err) // rotten corpus
|
||||
} |
||||
} |
||||
// And do the fuzzing
|
||||
f.Fuzz(func(t *testing.T, data []byte) { |
||||
var ( |
||||
jt = vm.NewPragueEOFInstructionSetForTesting() |
||||
c vm.Container |
||||
) |
||||
cpy := common.CopyBytes(data) |
||||
if err := c.UnmarshalBinary(data, true); err == nil { |
||||
c.ValidateCode(&jt, true) |
||||
if have := c.MarshalBinary(); !bytes.Equal(have, data) { |
||||
t.Fatal("Unmarshal-> Marshal failure!") |
||||
} |
||||
} |
||||
if err := c.UnmarshalBinary(data, false); err == nil { |
||||
c.ValidateCode(&jt, false) |
||||
if have := c.MarshalBinary(); !bytes.Equal(have, data) { |
||||
t.Fatal("Unmarshal-> Marshal failure!") |
||||
} |
||||
} |
||||
if !bytes.Equal(cpy, data) { |
||||
panic("data modified during unmarshalling") |
||||
} |
||||
}) |
||||
} |
||||
|
||||
func TestEofParseInitcode(t *testing.T) { |
||||
testEofParse(t, true, "testdata/eof/results.initcode.txt") |
||||
} |
||||
|
||||
func TestEofParseRegular(t *testing.T) { |
||||
testEofParse(t, false, "testdata/eof/results.regular.txt") |
||||
} |
||||
|
||||
func testEofParse(t *testing.T, isInitCode bool, wantFile string) { |
||||
var wantFn func() string |
||||
var wantLoc = 0 |
||||
{ // Configure the want-reader
|
||||
wants, err := os.Open(wantFile) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
scanner := bufio.NewScanner(wants) |
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024) |
||||
wantFn = func() string { |
||||
if scanner.Scan() { |
||||
wantLoc++ |
||||
return scanner.Text() |
||||
} |
||||
return "end of file reached" |
||||
} |
||||
} |
||||
|
||||
for i := 0; ; i++ { |
||||
fname := fmt.Sprintf("testdata/eof/eof_corpus_%d.txt", i) |
||||
corpus, err := os.Open(fname) |
||||
if err != nil { |
||||
break |
||||
} |
||||
t.Logf("# Reading seed data from %v", fname) |
||||
scanner := bufio.NewScanner(corpus) |
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024) |
||||
line := 1 |
||||
for scanner.Scan() { |
||||
s := scanner.Text() |
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") { |
||||
s = s[2:] |
||||
} |
||||
b, err := hex.DecodeString(s) |
||||
if err != nil { |
||||
panic(err) // rotten corpus
|
||||
} |
||||
have := "OK" |
||||
if _, err := parse(b, isInitCode); err != nil { |
||||
have = fmt.Sprintf("ERR: %v", err) |
||||
} |
||||
if false { // Change this to generate the want-output
|
||||
fmt.Printf("%v\n", have) |
||||
} else { |
||||
want := wantFn() |
||||
if have != want { |
||||
if len(want) > 100 { |
||||
want = want[:100] |
||||
} |
||||
if len(b) > 100 { |
||||
b = b[:100] |
||||
} |
||||
t.Errorf("%v:%d\n%v\ninput %x\nisInit: %v\nhave: %q\nwant: %q\n", |
||||
fname, line, fmt.Sprintf("%v:%d", wantFile, wantLoc), b, isInitCode, have, want) |
||||
} |
||||
} |
||||
line++ |
||||
} |
||||
corpus.Close() |
||||
} |
||||
} |
||||
|
||||
func BenchmarkEofParse(b *testing.B) { |
||||
corpus, err := os.Open("testdata/eof/eof_benches.txt") |
||||
if err != nil { |
||||
b.Fatal(err) |
||||
} |
||||
defer corpus.Close() |
||||
scanner := bufio.NewScanner(corpus) |
||||
scanner.Buffer(make([]byte, 1024), 10*1024*1024) |
||||
line := 1 |
||||
for scanner.Scan() { |
||||
s := scanner.Text() |
||||
if len(s) >= 2 && strings.HasPrefix(s, "0x") { |
||||
s = s[2:] |
||||
} |
||||
data, err := hex.DecodeString(s) |
||||
if err != nil { |
||||
b.Fatal(err) // rotten corpus
|
||||
} |
||||
b.Run(fmt.Sprintf("test-%d", line), func(b *testing.B) { |
||||
b.ReportAllocs() |
||||
b.SetBytes(int64(len(data))) |
||||
for i := 0; i < b.N; i++ { |
||||
_, _ = parse(data, false) |
||||
} |
||||
}) |
||||
line++ |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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 |
||||
} |
@ -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} |
||||
} |
@ -1,74 +0,0 @@ |
||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package stateless |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
) |
||||
|
||||
var _ = (*extWitnessMarshalling)(nil) |
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (e extWitness) MarshalJSON() ([]byte, error) { |
||||
type extWitness struct { |
||||
Block *types.Block `json:"block" gencodec:"required"` |
||||
Headers []*types.Header `json:"headers" gencodec:"required"` |
||||
Codes []hexutil.Bytes `json:"codes"` |
||||
State []hexutil.Bytes `json:"state"` |
||||
} |
||||
var enc extWitness |
||||
enc.Block = e.Block |
||||
enc.Headers = e.Headers |
||||
if e.Codes != nil { |
||||
enc.Codes = make([]hexutil.Bytes, len(e.Codes)) |
||||
for k, v := range e.Codes { |
||||
enc.Codes[k] = v |
||||
} |
||||
} |
||||
if e.State != nil { |
||||
enc.State = make([]hexutil.Bytes, len(e.State)) |
||||
for k, v := range e.State { |
||||
enc.State[k] = v |
||||
} |
||||
} |
||||
return json.Marshal(&enc) |
||||
} |
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (e *extWitness) UnmarshalJSON(input []byte) error { |
||||
type extWitness struct { |
||||
Block *types.Block `json:"block" gencodec:"required"` |
||||
Headers []*types.Header `json:"headers" gencodec:"required"` |
||||
Codes []hexutil.Bytes `json:"codes"` |
||||
State []hexutil.Bytes `json:"state"` |
||||
} |
||||
var dec extWitness |
||||
if err := json.Unmarshal(input, &dec); err != nil { |
||||
return err |
||||
} |
||||
if dec.Block == nil { |
||||
return errors.New("missing required field 'block' for extWitness") |
||||
} |
||||
e.Block = dec.Block |
||||
if dec.Headers == nil { |
||||
return errors.New("missing required field 'headers' for extWitness") |
||||
} |
||||
e.Headers = dec.Headers |
||||
if dec.Codes != nil { |
||||
e.Codes = make([][]byte, len(dec.Codes)) |
||||
for k, v := range dec.Codes { |
||||
e.Codes[k] = v |
||||
} |
||||
} |
||||
if dec.State != nil { |
||||
e.State = make([][]byte, len(dec.State)) |
||||
for k, v := range dec.State { |
||||
e.State[k] = v |
||||
} |
||||
} |
||||
return nil |
||||
} |
@ -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 |
||||
} |
@ -0,0 +1,157 @@ |
||||
// 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" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
|
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
) |
||||
|
||||
var ( |
||||
ErrRequestTypeNotSupported = errors.New("request type not supported") |
||||
errShortTypedRequest = errors.New("typed request too short") |
||||
) |
||||
|
||||
// Request types.
|
||||
const ( |
||||
DepositRequestType = 0x00 |
||||
) |
||||
|
||||
// Request is an EIP-7685 request object. It represents execution layer
|
||||
// triggered messages bound for the consensus layer.
|
||||
type Request struct { |
||||
inner RequestData |
||||
} |
||||
|
||||
// Type returns the EIP-7685 type of the request.
|
||||
func (r *Request) Type() byte { |
||||
return r.inner.requestType() |
||||
} |
||||
|
||||
// Inner returns the inner request data.
|
||||
func (r *Request) Inner() RequestData { |
||||
return r.inner |
||||
} |
||||
|
||||
// NewRequest creates a new request.
|
||||
func NewRequest(inner RequestData) *Request { |
||||
req := new(Request) |
||||
req.inner = inner.copy() |
||||
return req |
||||
} |
||||
|
||||
// Requests implements DerivableList for requests.
|
||||
type Requests []*Request |
||||
|
||||
// Len returns the length of s.
|
||||
func (s Requests) Len() int { return len(s) } |
||||
|
||||
// EncodeIndex encodes the i'th request to s.
|
||||
func (s Requests) EncodeIndex(i int, w *bytes.Buffer) { |
||||
s[i].encode(w) |
||||
} |
||||
|
||||
// RequestData is the underlying data of a request.
|
||||
type RequestData interface { |
||||
requestType() byte |
||||
encode(*bytes.Buffer) error |
||||
decode([]byte) error |
||||
copy() RequestData // creates a deep copy and initializes all fields
|
||||
} |
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (r *Request) EncodeRLP(w io.Writer) error { |
||||
buf := encodeBufferPool.Get().(*bytes.Buffer) |
||||
defer encodeBufferPool.Put(buf) |
||||
buf.Reset() |
||||
if err := r.encode(buf); err != nil { |
||||
return err |
||||
} |
||||
return rlp.Encode(w, buf.Bytes()) |
||||
} |
||||
|
||||
// encode writes the canonical encoding of a request to w.
|
||||
func (r *Request) encode(w *bytes.Buffer) error { |
||||
w.WriteByte(r.Type()) |
||||
return r.inner.encode(w) |
||||
} |
||||
|
||||
// MarshalBinary returns the canonical encoding of the request.
|
||||
func (r *Request) MarshalBinary() ([]byte, error) { |
||||
var buf bytes.Buffer |
||||
err := r.encode(&buf) |
||||
return buf.Bytes(), err |
||||
} |
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (r *Request) DecodeRLP(s *rlp.Stream) error { |
||||
kind, size, err := s.Kind() |
||||
switch { |
||||
case err != nil: |
||||
return err |
||||
case kind == rlp.List: |
||||
return fmt.Errorf("untyped request") |
||||
case kind == rlp.Byte: |
||||
return errShortTypedRequest |
||||
default: |
||||
// First read the request payload bytes into a temporary buffer.
|
||||
b, buf, err := getPooledBuffer(size) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer encodeBufferPool.Put(buf) |
||||
if err := s.ReadBytes(b); err != nil { |
||||
return err |
||||
} |
||||
// Now decode the inner request.
|
||||
inner, err := r.decode(b) |
||||
if err == nil { |
||||
r.inner = inner |
||||
} |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// UnmarshalBinary decodes the canonical encoding of requests.
|
||||
func (r *Request) UnmarshalBinary(b []byte) error { |
||||
inner, err := r.decode(b) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
r.inner = inner |
||||
return nil |
||||
} |
||||
|
||||
// decode decodes a request from the canonical format.
|
||||
func (r *Request) decode(b []byte) (RequestData, error) { |
||||
if len(b) <= 1 { |
||||
return nil, errShortTypedRequest |
||||
} |
||||
var inner RequestData |
||||
switch b[0] { |
||||
case DepositRequestType: |
||||
inner = new(Deposit) |
||||
default: |
||||
return nil, ErrRequestTypeNotSupported |
||||
} |
||||
err := inner.decode(b[1:]) |
||||
return inner, err |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue