mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
Coreth 0.8.16 rc2 (#216)
* Unwrap ETHChain * hook in vm.ethConfig * statesync for subnet-evm * remove unused configJSON * add err check * add synchronization * add default config values * delay initialization of gossiper * Revert "add synchronization" This reverts commit 2e8c93e58d7853de15a21ebd1460a3cd3858a6af. * fix test * fix statesync code prefix issue * fix comment * wip * wip2 * add coreth changes * Merge branch 'master' into coreth-0.8.16-rc2 * updates * remove coreth from go.mod * fix linter * fix * update gas prices (?) * fix tracer test * move dependabot yml to upper file * more changes ported in from coreth 0.8.16 * Update sync/README.md Co-authored-by: Ceyhun Onur <[email protected]> * Update sync/README.md Co-authored-by: Ceyhun Onur <[email protected]> * Update sync/README.md Co-authored-by: Ceyhun Onur <[email protected]> * Update core/rawdb/accessors_state_sync.go * port missing file from coreth * fix-import * subscribe before goroutine * Rebase apply coreth diff onto statesync * fix EOF in tests * Nits (#265) * Nits * Add EIP-1559 link to readme * Fix linting introduced by merge
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -21,6 +21,7 @@ linters:
|
||||
- misspell
|
||||
- unconvert
|
||||
- varcheck
|
||||
- whitespace
|
||||
|
||||
linters-settings:
|
||||
gofmt:
|
||||
|
||||
+4
-19
@@ -234,7 +234,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||
case "constructor":
|
||||
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||
case "function":
|
||||
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
||||
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
||||
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
||||
case "fallback":
|
||||
// New introduced function type in v0.6.0, check more detail
|
||||
@@ -254,9 +254,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||
case "event":
|
||||
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
||||
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
||||
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||
case "error":
|
||||
// Errors cannot be overloaded or overridden but are inherited,
|
||||
// no need to resolve the name conflict here.
|
||||
abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
|
||||
default:
|
||||
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||
@@ -321,20 +323,3 @@ func UnpackRevert(data []byte) (string, error) {
|
||||
}
|
||||
return unpacked[0].(string), nil
|
||||
}
|
||||
|
||||
// overloadedName returns the next available name for a given thing.
|
||||
// Needed since solidity allows for overloading.
|
||||
//
|
||||
// e.g. if the abi contains Methods send, send1
|
||||
// overloadedName would return send2 for input send.
|
||||
//
|
||||
// overloadedName works for methods, events and errors.
|
||||
func overloadedName(rawName string, isAvail func(string) bool) string {
|
||||
name := rawName
|
||||
ok := isAvail(name)
|
||||
for idx := 0; ok; idx++ {
|
||||
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||
ok = isAvail(name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -96,7 +96,8 @@ var (
|
||||
TupleF, _ = NewType("tuple", "struct Overloader.F", []ArgumentMarshaling{
|
||||
{Name: "_f", Type: "uint256"},
|
||||
{Name: "__f", Type: "uint256"},
|
||||
{Name: "f", Type: "uint256"}})
|
||||
{Name: "f", Type: "uint256"},
|
||||
})
|
||||
)
|
||||
|
||||
var methods = map[string]Method{
|
||||
@@ -884,12 +885,12 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
||||
|
||||
func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||
// Two methods have the same name
|
||||
var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
|
||||
abiJSON := `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
|
||||
abi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||
hexdata := `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||
data, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1048,9 +1049,7 @@ func TestABI_EventById(t *testing.T) {
|
||||
}
|
||||
if event == nil {
|
||||
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
||||
}
|
||||
|
||||
if event.ID != topicID {
|
||||
} else if event.ID != topicID {
|
||||
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID.Hex(), topicID.Hex(), testnum)
|
||||
}
|
||||
|
||||
@@ -1143,7 +1142,7 @@ func TestUnnamedEventParam(t *testing.T) {
|
||||
func TestUnpackRevert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var cases = []struct {
|
||||
cases := []struct {
|
||||
input string
|
||||
expect string
|
||||
expectErr error
|
||||
|
||||
@@ -28,6 +28,7 @@ package abi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -88,8 +89,8 @@ func (arguments Arguments) isTuple() bool {
|
||||
// Unpack performs the operation hexdata -> Go format.
|
||||
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
||||
if len(data) == 0 {
|
||||
if len(arguments) != 0 {
|
||||
return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
}
|
||||
return make([]interface{}, 0), nil
|
||||
}
|
||||
@@ -100,11 +101,11 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
||||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||
// Make sure map is not nil
|
||||
if v == nil {
|
||||
return fmt.Errorf("abi: cannot unpack into a nil map")
|
||||
return errors.New("abi: cannot unpack into a nil map")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
if len(arguments) != 0 {
|
||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
}
|
||||
return nil // Nothing to unmarshal, return
|
||||
}
|
||||
@@ -125,8 +126,8 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||
}
|
||||
if len(values) == 0 {
|
||||
if len(arguments) != 0 {
|
||||
return fmt.Errorf("abi: attempting to copy no values while %d arguments are expected", len(arguments))
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return errors.New("abi: attempting to copy no values while arguments are expected")
|
||||
}
|
||||
return nil // Nothing to copy, return
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/accounts"
|
||||
@@ -55,7 +54,7 @@ var ErrNotAuthorized = errors.New("not authorized to sign this account")
|
||||
// Deprecated: Use NewTransactorWithChainID instead.
|
||||
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
|
||||
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
|
||||
json, err := ioutil.ReadAll(keyin)
|
||||
json, err := io.ReadAll(keyin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,7 +115,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
||||
// NewTransactorWithChainID is a utility method to easily create a transaction signer from
|
||||
// an encrypted json key stream and the associated passphrase.
|
||||
func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
|
||||
json, err := ioutil.ReadAll(keyin)
|
||||
json, err := io.ReadAll(keyin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
|
||||
database: database,
|
||||
blockchain: blockchain,
|
||||
config: genesis.Config,
|
||||
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
|
||||
}
|
||||
backend.events = filters.NewEventSystem(&filterBackend{database, blockchain, backend}, false)
|
||||
backend.rollback(blockchain.CurrentBlock())
|
||||
return backend
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func (b *SimulatedBackend) Close() error {
|
||||
|
||||
// Commit imports all the pending transactions as a single block and starts a
|
||||
// fresh new state.
|
||||
func (b *SimulatedBackend) Commit(accept bool) {
|
||||
func (b *SimulatedBackend) Commit(accept bool) common.Hash {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
@@ -151,9 +151,13 @@ func (b *SimulatedBackend) Commit(accept bool) {
|
||||
}
|
||||
b.blockchain.DrainAcceptorQueue()
|
||||
}
|
||||
blockHash := b.acceptedBlock.Hash()
|
||||
|
||||
// Using the last inserted block here makes it possible to build on a side
|
||||
// chain after a fork.
|
||||
b.rollback(b.acceptedBlock)
|
||||
|
||||
return blockHash
|
||||
}
|
||||
|
||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||
@@ -724,7 +728,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query interfaces.Filt
|
||||
var filter *filters.Filter
|
||||
if query.BlockHash != nil {
|
||||
// Block filter requested, construct a single-shot filter
|
||||
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
|
||||
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain, b}, *query.BlockHash, query.Addresses, query.Topics)
|
||||
} else {
|
||||
// Initialize unset filter boundaries to run from genesis to chain head
|
||||
from := int64(0)
|
||||
@@ -736,7 +740,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query interfaces.Filt
|
||||
to = query.ToBlock.Int64()
|
||||
}
|
||||
// Construct the range filter
|
||||
filter, _ = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
|
||||
filter, _ = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain, b}, from, to, query.Addresses, query.Topics)
|
||||
}
|
||||
// Run the filter and return all the logs
|
||||
logs, err := filter.Logs(ctx)
|
||||
@@ -857,8 +861,9 @@ func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
||||
// filterBackend implements filters.Backend to support filtering for logs without
|
||||
// taking bloom-bits acceleration structures into account.
|
||||
type filterBackend struct {
|
||||
db ethdb.Database
|
||||
bc *core.BlockChain
|
||||
db ethdb.Database
|
||||
bc *core.BlockChain
|
||||
backend *SimulatedBackend
|
||||
}
|
||||
|
||||
func (fb *filterBackend) SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||
|
||||
@@ -673,8 +673,7 @@ func TestHeaderByNumber(t *testing.T) {
|
||||
}
|
||||
if latestBlockHeader == nil {
|
||||
t.Errorf("received a nil block header")
|
||||
}
|
||||
if latestBlockHeader.Number.Uint64() != uint64(0) {
|
||||
} else if latestBlockHeader.Number.Uint64() != uint64(0) {
|
||||
t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
|
||||
}
|
||||
|
||||
@@ -1362,3 +1361,42 @@ contract Callable {
|
||||
// t.Errorf("TX included in wrong block: %d", h)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestCommitReturnValue(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
startBlockHeight := sim.blockchain.CurrentBlock().NumberU64()
|
||||
|
||||
// Test if Commit returns the correct block hash
|
||||
h1 := sim.Commit(true)
|
||||
if h1 != sim.blockchain.CurrentBlock().Hash() {
|
||||
t.Error("Commit did not return the hash of the last block.")
|
||||
}
|
||||
|
||||
// Create a block in the original chain (containing a transaction to force different block hashes)
|
||||
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
_tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
tx, _ := types.SignTx(_tx, types.HomesteadSigner{}, testKey)
|
||||
sim.SendTransaction(context.Background(), tx)
|
||||
h2 := sim.Commit(false)
|
||||
|
||||
// Create another block in the original chain
|
||||
sim.Commit(false)
|
||||
|
||||
// Fork at the first bock
|
||||
if err := sim.Fork(context.Background(), h1); err != nil {
|
||||
t.Errorf("forking: %v", err)
|
||||
}
|
||||
|
||||
// Test if Commit returns the correct block hash after the reorg
|
||||
h2fork := sim.Commit(false)
|
||||
if h2 == h2fork {
|
||||
t.Error("The block in the fork and the original block are the same block!")
|
||||
}
|
||||
if sim.blockchain.GetHeader(h2fork, startBlockHeight+2) == nil {
|
||||
t.Error("Could not retrieve the just created block (side-chain)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||
return ErrNoAcceptedState
|
||||
}
|
||||
output, err = pb.AcceptedCallContract(ctx, msg)
|
||||
if err == nil && len(output) == 0 {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(output) == 0 {
|
||||
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||
if code, err = pb.AcceptedCodeAt(ctx, c.address); err != nil {
|
||||
return err
|
||||
|
||||
+165
-11
@@ -28,6 +28,7 @@ package bind_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -85,34 +86,50 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac
|
||||
}
|
||||
|
||||
type mockCaller struct {
|
||||
codeAtBlockNumber *big.Int
|
||||
callContractBlockNumber *big.Int
|
||||
acceptedCodeAtCalled bool
|
||||
acceptedCallContractCalled bool
|
||||
codeAtBlockNumber *big.Int
|
||||
callContractBlockNumber *big.Int
|
||||
callContractBytes []byte
|
||||
callContractErr error
|
||||
codeAtBytes []byte
|
||||
codeAtErr error
|
||||
}
|
||||
|
||||
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
mc.codeAtBlockNumber = blockNumber
|
||||
return []byte{1, 2, 3}, nil
|
||||
return mc.codeAtBytes, mc.codeAtErr
|
||||
}
|
||||
|
||||
func (mc *mockCaller) CallContract(ctx context.Context, call interfaces.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
mc.callContractBlockNumber = blockNumber
|
||||
return nil, nil
|
||||
return mc.callContractBytes, mc.callContractErr
|
||||
}
|
||||
|
||||
func (mc *mockCaller) AcceptedCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||
type mockAcceptedCaller struct {
|
||||
*mockCaller
|
||||
acceptedCodeAtBytes []byte
|
||||
acceptedCodeAtErr error
|
||||
acceptedCodeAtCalled bool
|
||||
acceptedCallContractCalled bool
|
||||
acceptedCallContractBytes []byte
|
||||
acceptedCallContractErr error
|
||||
}
|
||||
|
||||
func (mc *mockAcceptedCaller) AcceptedCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||
mc.acceptedCodeAtCalled = true
|
||||
return nil, nil
|
||||
return mc.acceptedCodeAtBytes, mc.acceptedCodeAtErr
|
||||
}
|
||||
|
||||
func (mc *mockCaller) AcceptedCallContract(ctx context.Context, call interfaces.CallMsg) ([]byte, error) {
|
||||
func (mc *mockAcceptedCaller) AcceptedCallContract(ctx context.Context, call interfaces.CallMsg) ([]byte, error) {
|
||||
mc.acceptedCallContractCalled = true
|
||||
return nil, nil
|
||||
return mc.acceptedCallContractBytes, mc.acceptedCallContractErr
|
||||
}
|
||||
|
||||
func TestPassingBlockNumber(t *testing.T) {
|
||||
mc := &mockCaller{}
|
||||
mc := &mockAcceptedCaller{
|
||||
mockCaller: &mockCaller{
|
||||
codeAtBytes: []byte{1, 2, 3},
|
||||
},
|
||||
}
|
||||
|
||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||
Methods: map[string]abi.Method{
|
||||
@@ -351,3 +368,140 @@ func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
|
||||
Removed: false,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
method, methodWithArg := "something", "somethingArrrrg"
|
||||
tests := []struct {
|
||||
name, method string
|
||||
opts *bind.CallOpts
|
||||
mc bind.ContractCaller
|
||||
results *[]interface{}
|
||||
wantErr bool
|
||||
wantErrExact error
|
||||
}{{
|
||||
name: "ok not accepted",
|
||||
mc: &mockCaller{
|
||||
codeAtBytes: []byte{0},
|
||||
},
|
||||
method: method,
|
||||
}, {
|
||||
name: "ok accepted",
|
||||
mc: &mockAcceptedCaller{
|
||||
acceptedCodeAtBytes: []byte{0},
|
||||
},
|
||||
opts: &bind.CallOpts{
|
||||
Accepted: true,
|
||||
},
|
||||
method: method,
|
||||
}, {
|
||||
name: "pack error, no method",
|
||||
mc: new(mockCaller),
|
||||
method: "else",
|
||||
wantErr: true,
|
||||
}, {
|
||||
name: "interface error, accepted but not a AcceptedContractCaller",
|
||||
mc: new(mockCaller),
|
||||
opts: &bind.CallOpts{
|
||||
Accepted: true,
|
||||
},
|
||||
method: method,
|
||||
wantErrExact: bind.ErrNoAcceptedState,
|
||||
}, {
|
||||
name: "accepted call canceled",
|
||||
mc: &mockAcceptedCaller{
|
||||
acceptedCallContractErr: context.DeadlineExceeded,
|
||||
},
|
||||
opts: &bind.CallOpts{
|
||||
Accepted: true,
|
||||
},
|
||||
method: method,
|
||||
wantErrExact: context.DeadlineExceeded,
|
||||
}, {
|
||||
name: "accepted code at error",
|
||||
mc: &mockAcceptedCaller{
|
||||
acceptedCodeAtErr: errors.New(""),
|
||||
},
|
||||
opts: &bind.CallOpts{
|
||||
Accepted: true,
|
||||
},
|
||||
method: method,
|
||||
wantErr: true,
|
||||
}, {
|
||||
name: "no accepted code at",
|
||||
mc: new(mockAcceptedCaller),
|
||||
opts: &bind.CallOpts{
|
||||
Accepted: true,
|
||||
},
|
||||
method: method,
|
||||
wantErrExact: bind.ErrNoCode,
|
||||
}, {
|
||||
name: "call contract error",
|
||||
mc: &mockCaller{
|
||||
callContractErr: context.DeadlineExceeded,
|
||||
},
|
||||
method: method,
|
||||
wantErrExact: context.DeadlineExceeded,
|
||||
}, {
|
||||
name: "code at error",
|
||||
mc: &mockCaller{
|
||||
codeAtErr: errors.New(""),
|
||||
},
|
||||
method: method,
|
||||
wantErr: true,
|
||||
}, {
|
||||
name: "no code at",
|
||||
mc: new(mockCaller),
|
||||
method: method,
|
||||
wantErrExact: bind.ErrNoCode,
|
||||
}, {
|
||||
name: "unpack error missing arg",
|
||||
mc: &mockCaller{
|
||||
codeAtBytes: []byte{0},
|
||||
},
|
||||
method: methodWithArg,
|
||||
wantErr: true,
|
||||
}, {
|
||||
name: "interface unpack error",
|
||||
mc: &mockCaller{
|
||||
codeAtBytes: []byte{0},
|
||||
},
|
||||
method: method,
|
||||
results: &[]interface{}{0},
|
||||
wantErr: true,
|
||||
}}
|
||||
for _, test := range tests {
|
||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||
Methods: map[string]abi.Method{
|
||||
method: {
|
||||
Name: method,
|
||||
Outputs: abi.Arguments{},
|
||||
},
|
||||
methodWithArg: {
|
||||
Name: methodWithArg,
|
||||
Outputs: abi.Arguments{abi.Argument{}},
|
||||
},
|
||||
},
|
||||
}, test.mc, nil, nil)
|
||||
err := bc.Call(test.opts, test.results, test.method)
|
||||
if test.wantErr || test.wantErrExact != nil {
|
||||
if err == nil {
|
||||
t.Fatalf("%q expected error", test.name)
|
||||
}
|
||||
if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) {
|
||||
t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("%q unexpected error: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCrashers contains some strings which previously caused the abi codec to crash.
|
||||
func TestCrashers(t *testing.T) {
|
||||
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`))
|
||||
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`))
|
||||
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`))
|
||||
abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`))
|
||||
}
|
||||
|
||||
@@ -60,6 +60,43 @@ const (
|
||||
LangObjC
|
||||
)
|
||||
|
||||
func isKeyWord(arg string) bool {
|
||||
switch arg {
|
||||
case "break":
|
||||
case "case":
|
||||
case "chan":
|
||||
case "const":
|
||||
case "continue":
|
||||
case "default":
|
||||
case "defer":
|
||||
case "else":
|
||||
case "fallthrough":
|
||||
case "for":
|
||||
case "func":
|
||||
case "go":
|
||||
case "goto":
|
||||
case "if":
|
||||
case "import":
|
||||
case "interface":
|
||||
case "iota":
|
||||
case "map":
|
||||
case "make":
|
||||
case "new":
|
||||
case "package":
|
||||
case "range":
|
||||
case "return":
|
||||
case "select":
|
||||
case "struct":
|
||||
case "switch":
|
||||
case "type":
|
||||
case "var":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
|
||||
// to be used as is in client code, but rather as an intermediate struct which
|
||||
// enforces compile time type safety and naming convention opposed to having to
|
||||
@@ -130,7 +167,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||
copy(normalized.Inputs, original.Inputs)
|
||||
for j, input := range normalized.Inputs {
|
||||
if input.Name == "" {
|
||||
if input.Name == "" || isKeyWord(input.Name) {
|
||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||
}
|
||||
if hasStruct(input.Type) {
|
||||
@@ -175,12 +212,22 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||
eventIdentifiers[normalizedName] = true
|
||||
normalized.Name = normalizedName
|
||||
|
||||
used := make(map[string]bool)
|
||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||
copy(normalized.Inputs, original.Inputs)
|
||||
for j, input := range normalized.Inputs {
|
||||
if input.Name == "" {
|
||||
if input.Name == "" || isKeyWord(input.Name) {
|
||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||
}
|
||||
// Event is a bit special, we need to define event struct in binding,
|
||||
// ensure there is no camel-case-style name conflict.
|
||||
for index := 0; ; index++ {
|
||||
if !used[capitalise(normalized.Inputs[j].Name)] {
|
||||
used[capitalise(normalized.Inputs[j].Name)] = true
|
||||
break
|
||||
}
|
||||
normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
|
||||
}
|
||||
if hasStruct(input.Type) {
|
||||
bindStructType[lang](input.Type, structs)
|
||||
}
|
||||
@@ -202,7 +249,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||
|
||||
contracts[types[i]] = &tmplContract{
|
||||
Type: capitalise(types[i]),
|
||||
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
||||
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
|
||||
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
|
||||
Constructor: evmABI.Constructor,
|
||||
Calls: calls,
|
||||
@@ -478,15 +525,22 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||
if s, exist := structs[id]; exist {
|
||||
return s.Name
|
||||
}
|
||||
var fields []*tmplField
|
||||
var (
|
||||
names = make(map[string]bool)
|
||||
fields []*tmplField
|
||||
)
|
||||
for i, elem := range kind.TupleElems {
|
||||
field := bindStructTypeGo(*elem, structs)
|
||||
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
||||
name := capitalise(kind.TupleRawNames[i])
|
||||
name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
|
||||
names[name] = true
|
||||
fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
|
||||
}
|
||||
name := kind.TupleRawName
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("Struct%d", len(structs))
|
||||
}
|
||||
name = capitalise(name)
|
||||
|
||||
structs[id] = &tmplStruct{
|
||||
Name: name,
|
||||
Fields: fields,
|
||||
|
||||
@@ -104,7 +104,6 @@ func golangBindingsFailure(t *testing.T) {
|
||||
if tt.errorMsg != err.Error() {
|
||||
t.Fatalf("test %d: expected Err %s but got actual Err: %s", i, tt.errorMsg, err.Error())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ package bind
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -1977,6 +1976,91 @@ var bindTests = []struct {
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: `NameConflict`,
|
||||
contract: `
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
contract oracle {
|
||||
struct request {
|
||||
bytes data;
|
||||
bytes _data;
|
||||
}
|
||||
event log (int msg, int _msg);
|
||||
function addRequest(request memory req) public pure {}
|
||||
function getRequest() pure public returns (request memory) {
|
||||
return request("", "");
|
||||
}
|
||||
}
|
||||
`,
|
||||
bytecode: []string{"0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033"},
|
||||
abi: []string{`[ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "int256", "name": "msg", "type": "int256" }, { "indexed": false, "internalType": "int256", "name": "_msg", "type": "int256" } ], "name": "log", "type": "event" }, { "inputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "req", "type": "tuple" } ], "name": "addRequest", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getRequest", "outputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "", "type": "tuple" } ], "stateMutability": "pure", "type": "function" } ]`},
|
||||
imports: `
|
||||
"math/big"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
|
||||
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
|
||||
"github.com/ava-labs/subnet-evm/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
tester: `
|
||||
var (
|
||||
gasCeil = uint64(30000000) // Note: from geth's ethconfig.Defaults.Miner.GasCeil
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil)
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
_, tx, _, err := DeployNameConflict(user, sim)
|
||||
if err != nil {
|
||||
t.Fatalf("DeployNameConflict() got err %v; want nil err", err)
|
||||
}
|
||||
sim.Commit(true)
|
||||
|
||||
if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
|
||||
t.Logf("Deployment tx: %+v", tx)
|
||||
t.Errorf("bind.WaitDeployed(nil, %T, <deployment tx>) got err %v; want nil err", sim, err)
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "RangeKeyword",
|
||||
contract: `
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
contract keywordcontract {
|
||||
function functionWithKeywordParameter(range uint256) public pure {}
|
||||
}
|
||||
`,
|
||||
bytecode: []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"},
|
||||
abi: []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
||||
imports: `
|
||||
"math/big"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
|
||||
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
|
||||
"github.com/ava-labs/subnet-evm/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
tester: `
|
||||
var (
|
||||
gasCeil = uint64(30000000) // Note: from geth's ethconfig.Defaults.Miner.GasCeil
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil)
|
||||
)
|
||||
_, tx, _, err := DeployRangeKeyword(user, sim)
|
||||
if err != nil {
|
||||
t.Fatalf("error deploying contract: %v", err)
|
||||
}
|
||||
sim.Commit(true)
|
||||
|
||||
if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
|
||||
t.Errorf("error deploying the contract: %v", err)
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
// The binding tests have been modified to run in two separate test
|
||||
@@ -1998,14 +2082,10 @@ func golangBindings(t *testing.T, overload bool) {
|
||||
t.Skip("go sdk not found for testing")
|
||||
}
|
||||
// Create a temporary workspace for the test suite
|
||||
ws, err := ioutil.TempDir("", "binding-test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temporary workspace: %v", err)
|
||||
}
|
||||
// defer os.RemoveAll(ws)
|
||||
ws := t.TempDir()
|
||||
|
||||
pkg := filepath.Join(ws, "bindtest")
|
||||
if err = os.MkdirAll(pkg, 0o700); err != nil {
|
||||
if err := os.MkdirAll(pkg, 0o700); err != nil {
|
||||
t.Fatalf("failed to create package: %v", err)
|
||||
}
|
||||
// Generate the test suite for all the contracts
|
||||
@@ -2030,7 +2110,7 @@ func golangBindings(t *testing.T, overload bool) {
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: failed to generate binding: %v", i, err)
|
||||
}
|
||||
if err = ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0o600); err != nil {
|
||||
if err = os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0o600); err != nil {
|
||||
t.Fatalf("test %d: failed to write binding: %v", i, err)
|
||||
}
|
||||
// Generate the test file with the injected test code
|
||||
@@ -2046,7 +2126,7 @@ func golangBindings(t *testing.T, overload bool) {
|
||||
%s
|
||||
}
|
||||
`, tt.imports, tt.name, tt.tester)
|
||||
if err := ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0o600); err != nil {
|
||||
if err := os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0o600); err != nil {
|
||||
t.Fatalf("test %d: failed to write tests: %v", i, err)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -171,7 +171,7 @@ var (
|
||||
}
|
||||
{{range $pattern, $name := .Libraries}}
|
||||
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
||||
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
|
||||
{{end}}
|
||||
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||
if err != nil {
|
||||
|
||||
@@ -40,11 +40,13 @@ type Error struct {
|
||||
Name string
|
||||
Inputs Arguments
|
||||
str string
|
||||
|
||||
// Sig contains the string signature according to the ABI spec.
|
||||
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||
// e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||
// Please note that "int" is substitute for its canonical representation "int256"
|
||||
Sig string
|
||||
// ID returns the canonical representation of the event's signature used by the
|
||||
|
||||
// ID returns the canonical representation of the error's signature used by the
|
||||
// abi definition to identify event names and types.
|
||||
ID common.Hash
|
||||
}
|
||||
|
||||
@@ -32,9 +32,7 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
errBadBool = errors.New("abi: improperly encoded boolean value")
|
||||
)
|
||||
var errBadBool = errors.New("abi: improperly encoded boolean value")
|
||||
|
||||
// formatSliceString formats the reflection kind with the given slice size
|
||||
// and returns a formatted string representation.
|
||||
@@ -83,7 +81,6 @@ func typeCheck(t Type, value reflect.Value) error {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// typeErr returns a formatted type casting error.
|
||||
|
||||
@@ -171,7 +171,6 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEventTupleUnpack(t *testing.T) {
|
||||
|
||||
type EventTransfer struct {
|
||||
Value *big.Int
|
||||
}
|
||||
|
||||
@@ -230,7 +230,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||
|
||||
// second round ~~~
|
||||
for _, argName := range argNames {
|
||||
|
||||
structFieldName := ToCamelCase(argName)
|
||||
|
||||
if structFieldName == "" {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// Copyright 2020 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
|
||||
|
||||
+42
-24
@@ -33,6 +33,8 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
@@ -69,10 +71,8 @@ type Type struct {
|
||||
TupleType reflect.Type // Underlying struct of the tuple
|
||||
}
|
||||
|
||||
var (
|
||||
// typeRegex parses the abi sub types
|
||||
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
|
||||
)
|
||||
// typeRegex parses the abi sub types
|
||||
var typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
|
||||
|
||||
// NewType creates a new reflection type of abi type given in t.
|
||||
func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||
@@ -171,19 +171,24 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||
elems []*Type
|
||||
names []string
|
||||
expression string // canonical parameter expression
|
||||
used = make(map[string]bool)
|
||||
)
|
||||
expression += "("
|
||||
overloadedNames := make(map[string]string)
|
||||
for idx, c := range components {
|
||||
cType, err := NewType(c.Type, c.InternalType, c.Components)
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
fieldName, err := overloadedArgName(c.Name, overloadedNames)
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
name := ToCamelCase(c.Name)
|
||||
if name == "" {
|
||||
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
|
||||
}
|
||||
fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
|
||||
|
||||
used[fieldName] = true
|
||||
if !isValidFieldName(fieldName) {
|
||||
return Type{}, fmt.Errorf("field %d has invalid name", idx)
|
||||
}
|
||||
overloadedNames[fieldName] = fieldName
|
||||
fields = append(fields, reflect.StructField{
|
||||
Name: fieldName, // reflect.StructOf will panic for any exported field.
|
||||
Type: cType.GetType(),
|
||||
@@ -211,7 +216,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
|
||||
// Foo.Bar type definition is not allowed in golang,
|
||||
// convert the format to FooBar
|
||||
typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1)
|
||||
typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
|
||||
}
|
||||
|
||||
case "function":
|
||||
@@ -260,20 +265,6 @@ func (t Type) GetType() reflect.Type {
|
||||
}
|
||||
}
|
||||
|
||||
func overloadedArgName(rawName string, names map[string]string) (string, error) {
|
||||
fieldName := ToCamelCase(rawName)
|
||||
if fieldName == "" {
|
||||
return "", errors.New("abi: purely anonymous or underscored field is not supported")
|
||||
}
|
||||
// Handle overloaded fieldNames
|
||||
_, ok := names[fieldName]
|
||||
for idx := 0; ok; idx++ {
|
||||
fieldName = fmt.Sprintf("%s%d", ToCamelCase(rawName), idx)
|
||||
_, ok = names[fieldName]
|
||||
}
|
||||
return fieldName, nil
|
||||
}
|
||||
|
||||
// String implements Stringer.
|
||||
func (t Type) String() (out string) {
|
||||
return t.stringKind
|
||||
@@ -409,3 +400,30 @@ func getTypeSize(t Type) int {
|
||||
}
|
||||
return 32
|
||||
}
|
||||
|
||||
// isLetter reports whether a given 'rune' is classified as a Letter.
|
||||
// This method is copied from reflect/type.go
|
||||
func isLetter(ch rune) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
|
||||
}
|
||||
|
||||
// isValidFieldName checks if a string is a valid (struct) field name or not.
|
||||
//
|
||||
// According to the language spec, a field name should be an identifier.
|
||||
//
|
||||
// identifier = letter { letter | unicode_digit } .
|
||||
// letter = unicode_letter | "_" .
|
||||
// This method is copied from reflect/type.go
|
||||
func isValidFieldName(fieldName string) bool {
|
||||
for i, c := range fieldName {
|
||||
if i == 0 && !isLetter(c) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !(isLetter(c) || unicode.IsDigit(c)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return len(fieldName) > 0
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||
|
||||
// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
|
||||
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
||||
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
|
||||
bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
|
||||
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
||||
outputLength := big.NewInt(int64(len(output)))
|
||||
|
||||
@@ -277,11 +277,10 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
||||
}
|
||||
|
||||
offsetEnd := int(bigOffsetEnd.Uint64())
|
||||
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
|
||||
lengthBig := new(big.Int).SetBytes(output[offsetEnd-32 : offsetEnd])
|
||||
|
||||
totalSize := new(big.Int).Add(bigOffsetEnd, lengthBig)
|
||||
|
||||
totalSize := big.NewInt(0)
|
||||
totalSize.Add(totalSize, bigOffsetEnd)
|
||||
totalSize.Add(totalSize, lengthBig)
|
||||
if totalSize.BitLen() > 63 {
|
||||
return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
|
||||
}
|
||||
@@ -296,7 +295,7 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
||||
|
||||
// tuplePointsTo resolves the location reference for dynamic tuple.
|
||||
func tuplePointsTo(index int, output []byte) (start int, err error) {
|
||||
offset := big.NewInt(0).SetBytes(output[index : index+32])
|
||||
offset := new(big.Int).SetBytes(output[index : index+32])
|
||||
outputLen := big.NewInt(int64(len(output)))
|
||||
|
||||
if offset.Cmp(outputLen) > 0 {
|
||||
|
||||
@@ -44,7 +44,7 @@ import (
|
||||
func TestUnpack(t *testing.T) {
|
||||
for i, test := range packUnpackTests {
|
||||
t.Run(strconv.Itoa(i)+" "+test.def, func(t *testing.T) {
|
||||
//Unpack
|
||||
// Unpack
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
if err != nil {
|
||||
@@ -211,6 +211,23 @@ var unpackTests = []unpackTest{
|
||||
IntOne *big.Int
|
||||
}{big.NewInt(1)},
|
||||
},
|
||||
{
|
||||
def: `[{"type":"bool"}]`,
|
||||
enc: "",
|
||||
want: false,
|
||||
err: "abi: attempting to unmarshall an empty string while arguments are expected",
|
||||
},
|
||||
{
|
||||
def: `[{"type":"bytes32","indexed":true},{"type":"uint256","indexed":false}]`,
|
||||
enc: "",
|
||||
want: false,
|
||||
err: "abi: attempting to unmarshall an empty string while arguments are expected",
|
||||
},
|
||||
{
|
||||
def: `[{"type":"bool","indexed":true},{"type":"uint64","indexed":true}]`,
|
||||
enc: "",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
// TestLocalUnpackTests runs test specially designed only for unpacking.
|
||||
@@ -218,7 +235,7 @@ var unpackTests = []unpackTest{
|
||||
func TestLocalUnpackTests(t *testing.T) {
|
||||
for i, test := range unpackTests {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
//Unpack
|
||||
// Unpack
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
if err != nil {
|
||||
@@ -299,7 +316,7 @@ type methodMultiOutput struct {
|
||||
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
||||
const definition = `[
|
||||
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
||||
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
||||
expected := methodMultiOutput{big.NewInt(1), "hello"}
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
require.NoError(err)
|
||||
@@ -325,7 +342,7 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||
|
||||
abi, data, expected := methodMultiReturn(require.New(t))
|
||||
bigint := new(big.Int)
|
||||
var testCases = []struct {
|
||||
testCases := []struct {
|
||||
dest interface{}
|
||||
expected interface{}
|
||||
error string
|
||||
@@ -417,7 +434,7 @@ func TestMultiReturnWithStringArray(t *testing.T) {
|
||||
}
|
||||
buff := new(bytes.Buffer)
|
||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
|
||||
temp, _ := big.NewInt(0).SetString("30000000000000000000", 10)
|
||||
temp, _ := new(big.Int).SetString("30000000000000000000", 10)
|
||||
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
|
||||
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
|
||||
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
|
||||
@@ -485,7 +502,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||
// construct the test array, each 3 char element is joined with 61 '0' chars,
|
||||
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
|
||||
buff.Write(common.Hex2Bytes(strings.Join([]string{
|
||||
"", //empty, to apply the 61-char separator to the first element as well.
|
||||
"", // empty, to apply the 61-char separator to the first element as well.
|
||||
"111", "112", "113", "121", "122", "123",
|
||||
"211", "212", "213", "221", "222", "223",
|
||||
"311", "312", "313", "321", "322", "323",
|
||||
@@ -779,7 +796,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||
type r struct {
|
||||
Result v
|
||||
}
|
||||
var ret0 = new(r)
|
||||
ret0 := new(r)
|
||||
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
|
||||
|
||||
if err != nil {
|
||||
@@ -838,7 +855,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||
A *big.Int
|
||||
}
|
||||
var ret Ret
|
||||
var expected = Ret{
|
||||
expected := Ret{
|
||||
FieldS: S{
|
||||
A: big.NewInt(1),
|
||||
B: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// (c) 2022, Ava Labs, Inc.
|
||||
//
|
||||
// This file is a derived work, based on the go-ethereum library whose original
|
||||
// notices appear below.
|
||||
//
|
||||
// It is distributed under a license compatible with the licensing terms of the
|
||||
// original code from which it is derived.
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2022 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 abi
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ResolveNameConflict returns the next available name for a given thing.
|
||||
// This helper can be used for lots of purposes:
|
||||
//
|
||||
// - In solidity function overloading is supported, this function can fix
|
||||
// the name conflicts of overloaded functions.
|
||||
// - In golang binding generation, the parameter(in function, event, error,
|
||||
// and struct definition) name will be converted to camelcase style which
|
||||
// may eventually lead to name conflicts.
|
||||
//
|
||||
// Name conflicts are mostly resolved by adding number suffix.
|
||||
// e.g. if the abi contains Methods send, send1
|
||||
// ResolveNameConflict would return send2 for input send.
|
||||
func ResolveNameConflict(rawName string, used func(string) bool) string {
|
||||
name := rawName
|
||||
ok := used(name)
|
||||
for idx := 0; ok; idx++ {
|
||||
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||
ok = used(name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
Vendored
-4
@@ -162,10 +162,6 @@ func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain int
|
||||
log.Error("operation SelfDerive not supported on external signers")
|
||||
}
|
||||
|
||||
func (api *ExternalSigner) signHash(account accounts.Account, hash []byte) ([]byte, error) {
|
||||
return []byte{}, fmt.Errorf("operation not supported on external signers")
|
||||
}
|
||||
|
||||
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
|
||||
func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
||||
var res hexutil.Bytes
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60,
|
||||
var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
|
||||
|
||||
// DerivationPath represents the computer friendly version of a hierarchical
|
||||
// deterministic wallet account derivaion path.
|
||||
// deterministic wallet account derivation path.
|
||||
//
|
||||
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
// defines derivation paths to be of the form:
|
||||
|
||||
@@ -28,7 +28,6 @@ package keystore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -66,7 +65,6 @@ func TestWatchNewFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Ensure the watcher is started before adding any files.
|
||||
ks.Accounts()
|
||||
@@ -116,7 +114,7 @@ func TestWatchNoDir(t *testing.T) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Create the directory and copy a key file into it.
|
||||
os.MkdirAll(dir, 0700)
|
||||
os.MkdirAll(dir, 0o700)
|
||||
defer os.RemoveAll(dir)
|
||||
file := filepath.Join(dir, "aaa")
|
||||
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
||||
@@ -343,7 +341,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Create the directory and copy a key file into it.
|
||||
os.MkdirAll(dir, 0700)
|
||||
os.MkdirAll(dir, 0o700)
|
||||
defer os.RemoveAll(dir)
|
||||
file := filepath.Join(dir, "aaa")
|
||||
|
||||
@@ -392,11 +390,11 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
// needed so that modTime of `file` is different to its current value after ioutil.WriteFile
|
||||
// needed so that modTime of `file` is different to its current value after os.WriteFile
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
// Now replace file contents with crap
|
||||
if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
|
||||
if err := os.WriteFile(file, []byte("foo"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
@@ -409,9 +407,9 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
|
||||
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
|
||||
func forceCopyFile(dst, src string) error {
|
||||
data, err := ioutil.ReadFile(src)
|
||||
data, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(dst, data, 0644)
|
||||
return os.WriteFile(dst, data, 0o644)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -51,7 +50,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
||||
t0 := time.Now()
|
||||
|
||||
// List all the failes from the keystore folder
|
||||
files, err := ioutil.ReadDir(keyDir)
|
||||
files, err := os.ReadDir(keyDir)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -75,7 +74,11 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
||||
// Gather the set of all and fresly modified files
|
||||
all.Add(path)
|
||||
|
||||
modified := fi.ModTime()
|
||||
info, err := fi.Info()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
modified := info.ModTime()
|
||||
if modified.After(fc.lastMod) {
|
||||
mods.Add(path)
|
||||
}
|
||||
@@ -99,13 +102,13 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
||||
}
|
||||
|
||||
// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
|
||||
func nonKeyFile(fi os.FileInfo) bool {
|
||||
func nonKeyFile(fi os.DirEntry) bool {
|
||||
// Skip editor backups and UNIX-style hidden files.
|
||||
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
||||
return true
|
||||
}
|
||||
// Skip misc special files, directories (yes, symlinks too).
|
||||
if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
|
||||
if fi.IsDir() || !fi.Type().IsRegular() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -205,13 +204,13 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
||||
func writeTemporaryKeyFile(file string, content []byte) (string, error) {
|
||||
// Create the keystore directory with appropriate permissions
|
||||
// in case it is not present yet.
|
||||
const dirPerm = 0700
|
||||
const dirPerm = 0o700
|
||||
if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Atomic write: create a temporary hidden file first
|
||||
// then move it into place. TempFile assigns mode 0600.
|
||||
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||
f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -48,7 +47,6 @@ var testSigData = make([]byte, 32)
|
||||
|
||||
func TestKeyStore(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
a, err := ks.NewAccount("foo")
|
||||
if err != nil {
|
||||
@@ -61,8 +59,8 @@ func TestKeyStore(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
|
||||
}
|
||||
if runtime.GOOS != "windows" && stat.Mode() != 0600 {
|
||||
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
|
||||
if runtime.GOOS != "windows" && stat.Mode() != 0o600 {
|
||||
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0o600)
|
||||
}
|
||||
if !ks.HasAddress(a.Address) {
|
||||
t.Errorf("HasAccount(%x) should've returned true", a.Address)
|
||||
@@ -82,8 +80,7 @@ func TestKeyStore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
pass := "" // not used but required by API
|
||||
a1, err := ks.NewAccount(pass)
|
||||
@@ -99,8 +96,7 @@ func TestSign(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSignWithPassphrase(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
pass := "passwd"
|
||||
acc, err := ks.NewAccount(pass)
|
||||
@@ -127,8 +123,7 @@ func TestSignWithPassphrase(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTimedUnlock(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
pass := "foo"
|
||||
a1, err := ks.NewAccount(pass)
|
||||
@@ -162,8 +157,7 @@ func TestTimedUnlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOverrideUnlock(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
|
||||
pass := "foo"
|
||||
a1, err := ks.NewAccount(pass)
|
||||
@@ -203,8 +197,7 @@ func TestOverrideUnlock(t *testing.T) {
|
||||
|
||||
// This test should fail under -race if signing races the expiration goroutine.
|
||||
func TestSignRace(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
|
||||
// Create a test account.
|
||||
a1, err := ks.NewAccount("")
|
||||
@@ -232,8 +225,7 @@ func TestSignRace(t *testing.T) {
|
||||
// addition and removal of wallet event subscriptions.
|
||||
func TestWalletNotifierLifecycle(t *testing.T) {
|
||||
// Create a temporary kesytore to test with
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
|
||||
// Ensure that the notification updater is not running yet
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
@@ -294,8 +286,7 @@ type walletEvent struct {
|
||||
// or deleted from the keystore.
|
||||
func TestWalletNotifications(t *testing.T) {
|
||||
t.Skip("FLAKY")
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
|
||||
// Subscribe to the wallet feed and collect events.
|
||||
var (
|
||||
@@ -356,8 +347,7 @@ func TestWalletNotifications(t *testing.T) {
|
||||
|
||||
// TestImportExport tests the import functionality of a keystore.
|
||||
func TestImportECDSA(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", key)
|
||||
@@ -375,8 +365,7 @@ func TestImportECDSA(t *testing.T) {
|
||||
|
||||
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||
func TestImportExport(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
acc, err := ks.NewAccount("old")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create account: %v", acc)
|
||||
@@ -385,8 +374,7 @@ func TestImportExport(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to export account: %v", acc)
|
||||
}
|
||||
dir2, ks2 := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir2)
|
||||
_, ks2 := tmpKeyStore(t, true)
|
||||
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
||||
t.Errorf("importing with invalid password succeeded")
|
||||
}
|
||||
@@ -405,8 +393,7 @@ func TestImportExport(t *testing.T) {
|
||||
// TestImportRace tests the keystore on races.
|
||||
// This test should fail under -race if importing races.
|
||||
func TestImportRace(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
acc, err := ks.NewAccount("old")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create account: %v", acc)
|
||||
@@ -415,8 +402,7 @@ func TestImportRace(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to export account: %v", acc)
|
||||
}
|
||||
dir2, ks2 := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir2)
|
||||
_, ks2 := tmpKeyStore(t, true)
|
||||
var atom uint32
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
@@ -471,10 +457,7 @@ func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
||||
}
|
||||
|
||||
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
|
||||
d, err := ioutil.TempDir("", "eth-keystore-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d := t.TempDir()
|
||||
newKs := NewPlaintextKeyStore
|
||||
if encrypted {
|
||||
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
|
||||
|
||||
@@ -44,7 +44,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -92,7 +91,7 @@ type keyStorePassphrase struct {
|
||||
|
||||
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
|
||||
// Load the key from the keystore and decrypt its contents
|
||||
keyjson, err := ioutil.ReadFile(filename)
|
||||
keyjson, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -351,7 +350,6 @@ func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||
r := ensureInt(cryptoJSON.KDFParams["r"])
|
||||
p := ensureInt(cryptoJSON.KDFParams["p"])
|
||||
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
||||
|
||||
} else if cryptoJSON.KDF == "pbkdf2" {
|
||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -40,7 +40,7 @@ const (
|
||||
|
||||
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
|
||||
func TestKeyEncryptDecrypt(t *testing.T) {
|
||||
keyjson, err := ioutil.ReadFile("testdata/very-light-scrypt.json")
|
||||
keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestKeyEncryptDecrypt(t *testing.T) {
|
||||
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
|
||||
}
|
||||
// Recrypt with a new password and start over
|
||||
password += "new data appended"
|
||||
password += "new data appended" // nolint: gosec
|
||||
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
|
||||
t.Errorf("test %d: failed to recrypt key %v", i, err)
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -41,10 +39,7 @@ import (
|
||||
)
|
||||
|
||||
func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
||||
d, err := ioutil.TempDir("", "geth-keystore-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d := t.TempDir()
|
||||
if encrypted {
|
||||
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
|
||||
} else {
|
||||
@@ -54,8 +49,7 @@ func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
||||
}
|
||||
|
||||
func TestKeyStorePlain(t *testing.T) {
|
||||
dir, ks := tmpKeyStoreIface(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStoreIface(t, false)
|
||||
|
||||
pass := "" // not used but required by API
|
||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||
@@ -75,8 +69,7 @@ func TestKeyStorePlain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKeyStorePassphrase(t *testing.T) {
|
||||
dir, ks := tmpKeyStoreIface(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStoreIface(t, true)
|
||||
|
||||
pass := "foo"
|
||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||
@@ -96,8 +89,7 @@ func TestKeyStorePassphrase(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||
dir, ks := tmpKeyStoreIface(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
_, ks := tmpKeyStoreIface(t, true)
|
||||
|
||||
pass := "foo"
|
||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||
@@ -111,7 +103,6 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||
|
||||
func TestImportPreSaleKey(t *testing.T) {
|
||||
dir, ks := tmpKeyStoreIface(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// file content of a presale key file generated with:
|
||||
// python pyethsaletool.py genwallet
|
||||
|
||||
@@ -44,7 +44,7 @@ package scwallet
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -106,7 +106,7 @@ func (hub *Hub) readPairings() error {
|
||||
return err
|
||||
}
|
||||
|
||||
pairingData, err := ioutil.ReadAll(pairingFile)
|
||||
pairingData, err := io.ReadAll(pairingFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func (hub *Hub) readPairings() error {
|
||||
}
|
||||
|
||||
func (hub *Hub) writePairings() error {
|
||||
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755)
|
||||
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
||||
return err
|
||||
}
|
||||
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
||||
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
||||
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2)
|
||||
}
|
||||
|
||||
if len(response.Data) != scSecretLength {
|
||||
@@ -271,7 +271,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||
rapdu.deserialize(plainData)
|
||||
|
||||
if rapdu.Sw1 != sw1Ok {
|
||||
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||
return nil, fmt.Errorf("unexpected response status Cla=%#x, Ins=%#x, Sw=%#x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||
}
|
||||
|
||||
return rapdu, nil
|
||||
|
||||
@@ -177,7 +177,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
|
||||
}
|
||||
|
||||
if response.Sw1 != sw1Ok {
|
||||
return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||
return nil, fmt.Errorf("unexpected insecure response status Cla=%#x, Ins=%#x, Sw=%#x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
|
||||
@@ -42,9 +42,10 @@ func TestURLParsing(t *testing.T) {
|
||||
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
|
||||
}
|
||||
|
||||
_, err = parseURL("ethereum.org")
|
||||
if err == nil {
|
||||
t.Error("expected err, got: nil")
|
||||
for _, u := range []string{"ethereum.org", ""} {
|
||||
if _, err = parseURL(u); err == nil {
|
||||
t.Errorf("input %v, expected err, got: nil", u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Consensus
|
||||
|
||||
Disclaimer: the consensus package in subnet-evm is a complete misnomer.
|
||||
|
||||
The consensus package in go-ethereum handles block validation and specifically handles validating the PoW portion of consensus - thus the name.
|
||||
|
||||
Since AvalancheGo handles consensus for Subnet-EVM, Subnet-EVM is just the VM, but we keep the consensus package in place to handle part of the block verification process.
|
||||
|
||||
## Block Verification
|
||||
|
||||
The dummy consensus engine is responsible for performing verification on the header of a block. The engine verifies that all of the fields of the header are correct.
|
||||
|
||||
## Dynamic Fees
|
||||
|
||||
Subnet-EVM includes a dynamic fee algorithm based off of (EIP-1559)[https://eips.ethereum.org/EIPS/eip-1559]. This introduces a field to the block type called `BaseFee`. The Base Fee sets a minimum gas price for any transaction to be included in the block. For example, a transaction with a gas price of 49 gwei, will be invalid to include in a block with a base fee of 50 gwei.
|
||||
|
||||
The dynamic fee algorithm aims to adjust the base fee to handle network congestion. Subnet-EVM sets a target utilization on the network, and the dynamic fee algorithm adjusts the base fee accordingly. If the network operates above the target utilization, the dynamic fee algorithm will increase the base fee to make utilizing he network more expensive and bring overall utilization down. If the network operates below the target utilization, the dynamic fee algorithm will decrease the base fee to make it cheaper to use the network.
|
||||
|
||||
- EIP-1559 is intended for Ethereum where a block is produced roughly every 10s
|
||||
- The dynamic fee algorithm needs to handle the case that the network quiesces and there are no blocks for a long period of time
|
||||
- Since Subnet-EVM produces blocks at a different cadence, it adapts EIP-1559 to sum the amount of gas consumed within a 10 second interval instead of using only the amount of gas consumed in the parent block
|
||||
|
||||
## Consensus Engine Callbacks
|
||||
|
||||
The consensus engine is called while blocks are being both built and processed and Subnet-EVM adds callback functions into the dummy consensus engine to insert its own logic into these stages.
|
||||
|
||||
### FinalizeAndAssemble
|
||||
|
||||
The FinalizeAndAssemble callback is used as the final step in building a block within the miner package.
|
||||
|
||||
### Finalize
|
||||
|
||||
Finalize is called as the final step in processing a block [here](../../core/state_processor.go). Since either Finalize or FinalizeAndAssemble are called, but not both, when building or verifying/processing a block they need to perform the exact same processing/verification step to ensure that a block produced by the miner where FinalizeAndAssemble is called will be processed and verified in the same way when Finalize gets called.
|
||||
@@ -254,11 +254,20 @@ func (self *DummyEngine) verifyBlockFee(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Multiply the [txFeePremium] by the gasUsed in the transaction since this gives the total coin that was paid
|
||||
// above the amount required if the transaction had simply paid the minimum base fee for the block.
|
||||
//
|
||||
// Ex. LegacyTx paying a gas price of 100 gwei for 1M gas in a block with a base fee of 10 gwei.
|
||||
// Total Fee = 100 gwei * 1M gas
|
||||
// Minimum Fee = 10 gwei * 1M gas (minimum fee that would have been accepted for this transaction)
|
||||
// Fee Premium = 90 gwei
|
||||
// Total Overpaid = 90 gwei * 1M gas
|
||||
|
||||
blockFeeContribution.Mul(txFeePremium, gasUsed.SetUint64(receipt.GasUsed))
|
||||
totalBlockFee.Add(totalBlockFee, blockFeeContribution)
|
||||
}
|
||||
// Calculate how much gas the [totalBlockFee] would purchase at the price level
|
||||
// set by this block.
|
||||
// set by the base fee of this block.
|
||||
blockGas := new(big.Int).Div(totalBlockFee, baseFee)
|
||||
|
||||
// Require that the amount of gas purchased by the effective tips within the block, [blockGas],
|
||||
@@ -282,6 +291,8 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate the expected blockGasCost for this block.
|
||||
// Note: this is a deterministic transtion that defines an exact block fee for this block.
|
||||
blockGasCost := calcBlockGasCost(
|
||||
feeConfig.TargetBlockRate,
|
||||
feeConfig.MinBlockGasCost,
|
||||
@@ -290,9 +301,11 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type
|
||||
parent.BlockGasCost,
|
||||
parent.Time, block.Time(),
|
||||
)
|
||||
// Verify the BlockGasCost set in the header matches the calculated value.
|
||||
if blockBlockGasCost := block.BlockGasCost(); blockBlockGasCost == nil || !blockBlockGasCost.IsUint64() || blockBlockGasCost.Cmp(blockGasCost) != 0 {
|
||||
return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost)
|
||||
}
|
||||
// Verify the block fee was paid.
|
||||
if err := self.verifyBlockFee(
|
||||
block.BaseFee(),
|
||||
block.BlockGasCost(),
|
||||
@@ -314,7 +327,7 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate the required block gas cost for this block.
|
||||
header.BlockGasCost = calcBlockGasCost(
|
||||
feeConfig.TargetBlockRate,
|
||||
feeConfig.MinBlockGasCost,
|
||||
@@ -323,6 +336,7 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader,
|
||||
parent.BlockGasCost,
|
||||
parent.Time, header.Time,
|
||||
)
|
||||
// Verify that this block covers the block fee.
|
||||
if err := self.verifyBlockFee(
|
||||
header.BaseFee,
|
||||
header.BlockGasCost,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Core Package
|
||||
|
||||
The core package maintains the backend for the blockchain, transaction pool, and maintains the required indexes for blocks, transactions, logs, and transaction receipts.
|
||||
|
||||
## Blockchain
|
||||
|
||||
The [BlockChain](./blockchain.go) struct handles the insertion of blocks into the maintained chain. It maintains a "canonical chain", which is essentially the preferred chain (the chain that ends with the block preferred by the AvalancheGo consensus engine).
|
||||
|
||||
When the consensus engine verifies blocks as they are ready to be issued into consensus, it calls `Verify()` on the ChainVM Block interface implemented [here](../plugin/evm/block.go). This calls `InsertBlockManual` on the BlockChain struct implemented in this package, which is the first entrypoint of a block into the blockchain.
|
||||
|
||||
InsertBlockManual verifies the block, inserts it into the state manager to track the merkle trie for the block, and adds it to the canonical chain if it extends the currently preferred chain.
|
||||
|
||||
Subnet-EVM adds functions for Accept and Reject, which take care of marking a block as finalized and performing garbage collection where possible.
|
||||
|
||||
The consensus engine can also call `SetPreference` on a VM to tell the VM that a specific block is preferred by the consensus engine to be accepted. This triggers a call to `reorg` the blockchain and set the newly preferred block as the preferred chain.
|
||||
|
||||
## Transaction Pool
|
||||
|
||||
The transaction pool maintains the set of transactions that need to be issued into a new block. The VM exposes APIs that allow clients to issue transactions into the transaction pool and also performs gossip across the network in order to send and receive pending transactions that need to be issued into a new block. The transaction pool asynchronously follows the preferred block of the `BlockChain` struct by subscribing to new head events and updating its state accordingly. When the transaction pool updates, it ensures that any transactions it contains are still valid to be issued on top of the new preferred block.
|
||||
|
||||
## State Manager
|
||||
|
||||
The State Manager manages the [TrieDB](../trie/database.go). The TrieDB tracks a merkle forest of all of the merkle tries for the last accepted block and processing blocks. When a block is processed, the state transition results in a new merkle trie added to the merkle forest. The State Manager can operate in either archival or pruning mode.
|
||||
|
||||
### Archival Mode
|
||||
|
||||
In archival mode, every merkle trie is written to disk so that the node maintains a complete history of all the blocks that it has processed.
|
||||
|
||||
### Pruning Mode
|
||||
|
||||
In pruning mode, the State Manager keeps a reference to merkle tries of processing blocks. When a block gets accepted, it stays in memory. When a block gets rejected, the state manager can dereference and clean up the no longer needed merkle trie. The State Manager does not immediately write the merkle trie to disk of a block when it gets accepted. Instead, at a regular interval (~4096 blocks) it writes the merkle trie to disk, so that it does not add the overhead of storing every accepted block's merkle trie to disk.
|
||||
+4
-15
@@ -28,7 +28,6 @@ package core
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -152,14 +151,11 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||
func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// Create the database in memory or in a temporary directory.
|
||||
var db ethdb.Database
|
||||
var err error
|
||||
if !disk {
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
} else {
|
||||
dir, err := ioutil.TempDir("", "eth-core-bench")
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
dir := b.TempDir()
|
||||
db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false)
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary database: %v", err)
|
||||
@@ -264,10 +260,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
|
||||
|
||||
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
dir, err := ioutil.TempDir("", "eth-chain-bench")
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary directory: %v", err)
|
||||
}
|
||||
dir := b.TempDir()
|
||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
|
||||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
@@ -279,11 +272,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||
}
|
||||
|
||||
func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||
dir, err := ioutil.TempDir("", "eth-chain-bench")
|
||||
if err != nil {
|
||||
b.Fatalf("cannot create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
dir := b.TempDir()
|
||||
|
||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
|
||||
if err != nil {
|
||||
|
||||
+19
-20
@@ -56,7 +56,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
acceptorQueueGauge = metrics.NewRegisteredGauge("blockchain/acceptor/queue/size", nil)
|
||||
acceptorQueueGauge = metrics.NewRegisteredGauge("blockchain/acceptor/queue/size", nil)
|
||||
processedBlockGasUsedCounter = metrics.NewRegisteredCounter("blockchain/blocks/gas/used/processed", nil)
|
||||
acceptedBlockGasUsedCounter = metrics.NewRegisteredCounter("blockchain/blocks/gas/used/accepted", nil)
|
||||
|
||||
ErrRefuseToCorruptArchiver = errors.New("node has operated with pruning disabled, shutting down to prevent missing tries")
|
||||
|
||||
@@ -545,20 +547,25 @@ func (bc *BlockChain) Export(w io.Writer) error {
|
||||
|
||||
// ExportN writes a subset of the active chain to the given writer.
|
||||
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
bc.chainmu.RLock()
|
||||
defer bc.chainmu.RUnlock()
|
||||
|
||||
if first > last {
|
||||
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
||||
}
|
||||
log.Info("Exporting batch of blocks", "count", last-first+1)
|
||||
|
||||
start, reported := time.Now(), time.Now()
|
||||
var (
|
||||
parentHash common.Hash
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
)
|
||||
for nr := first; nr <= last; nr++ {
|
||||
block := bc.GetBlockByNumber(nr)
|
||||
if block == nil {
|
||||
return fmt.Errorf("export failed on #%d: not found", nr)
|
||||
}
|
||||
if nr > first && block.ParentHash() != parentHash {
|
||||
return fmt.Errorf("export failed: chain reorg during export")
|
||||
}
|
||||
parentHash = block.Hash()
|
||||
if err := block.EncodeRLP(w); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -816,6 +823,8 @@ func (bc *BlockChain) Accept(block *types.Block) error {
|
||||
|
||||
bc.lastAccepted = block
|
||||
bc.addAcceptorQueue(block)
|
||||
acceptedBlockGasUsedCounter.Inc(int64(block.GasUsed()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1113,6 +1122,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error {
|
||||
"root", block.Root(), "baseFeePerGas", block.BaseFee(), "blockGasCost", block.BlockGasCost(),
|
||||
)
|
||||
|
||||
processedBlockGasUsedCounter.Inc(int64(block.GasUsed()))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1308,7 +1318,7 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e
|
||||
Chain config: %v
|
||||
|
||||
Number: %v
|
||||
Hash: 0x%x
|
||||
Hash: %#x
|
||||
%v
|
||||
|
||||
Error: %v
|
||||
@@ -1683,6 +1693,7 @@ func (bc *BlockChain) CleanBlockRootsAboveLastAccepted() error {
|
||||
// consensus engine will reject the lowest ancestor first. In this case, these blocks will not be considered acceptable in
|
||||
// the future.
|
||||
// Ex.
|
||||
//
|
||||
// A
|
||||
// / \
|
||||
// B C
|
||||
@@ -1758,22 +1769,10 @@ func (bc *BlockChain) ResetState(block *types.Block) error {
|
||||
|
||||
// Make sure the state associated with the block is available
|
||||
head := bc.CurrentBlock()
|
||||
if _, err := state.New(head.Root(), bc.stateCache, nil); err != nil {
|
||||
if !bc.HasState(head.Root()) {
|
||||
return fmt.Errorf("head state missing %d:%s", head.Number(), head.Hash())
|
||||
}
|
||||
|
||||
// Load any existing snapshot, regenerating it if loading failed
|
||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||
var err error
|
||||
// If we are starting from genesis, generate the original snapshot disk layer
|
||||
// up front, so we can use it while executing blocks in bootstrapping. This
|
||||
// also avoids a costly async generation process when reaching tip.
|
||||
async := bc.cacheConfig.SnapshotAsync && head.NumberU64() > 0
|
||||
log.Info("Initializing snapshots", "async", async)
|
||||
bc.snaps, err = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Hash(), head.Root(), async, true, bc.cacheConfig.SnapshotVerify)
|
||||
if err != nil {
|
||||
log.Error("failed to initialize snapshots", "headHash", head.Hash(), "headRoot", head.Root(), "err", err, "async", async)
|
||||
}
|
||||
}
|
||||
bc.initSnapshot(head)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -240,18 +240,6 @@ func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) {
|
||||
return bc.stateCache.ContractCode(common.Hash{}, hash)
|
||||
}
|
||||
|
||||
// ContractCodeWithPrefix retrieves a blob of data associated with a contract
|
||||
// hash either from ephemeral in-memory cache, or from persistent storage.
|
||||
//
|
||||
// If the code doesn't exist in the in-memory cache, check the storage with
|
||||
// new code scheme.
|
||||
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
|
||||
type codeReader interface {
|
||||
ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error)
|
||||
}
|
||||
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
|
||||
}
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
func (bc *BlockChain) State() (*state.StateDB, error) {
|
||||
return bc.StateAt(bc.CurrentBlock().Root())
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/consensus/dummy"
|
||||
@@ -510,11 +508,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||
// fmt.Println(tt.dump(true))
|
||||
|
||||
// Create a temporary persistent database
|
||||
datadir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary datadir: %v", err)
|
||||
}
|
||||
os.RemoveAll(datadir)
|
||||
datadir := t.TempDir()
|
||||
|
||||
db, err := rawdb.NewLevelDBDatabase(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
|
||||
@@ -32,7 +32,6 @@ package core
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -68,11 +67,7 @@ type snapshotTestBasic struct {
|
||||
|
||||
func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Block) {
|
||||
// Create a temporary persistent database
|
||||
datadir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary datadir: %v", err)
|
||||
}
|
||||
os.RemoveAll(datadir)
|
||||
datadir := t.TempDir()
|
||||
|
||||
db, err := rawdb.NewLevelDBDatabase(datadir, 0, 0, "", false)
|
||||
if err != nil {
|
||||
@@ -164,6 +159,7 @@ func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks [
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (basic *snapshotTestBasic) dump() string {
|
||||
buffer := new(strings.Builder)
|
||||
|
||||
@@ -321,60 +317,6 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||
snaptest.verify(t, newchain, blocks)
|
||||
}
|
||||
|
||||
// restartCrashSnapshotTest is the test type used to test this scenario:
|
||||
// - have a complete snapshot
|
||||
// - restart chain
|
||||
// - insert more blocks with enabling the snapshot
|
||||
// - commit the snapshot
|
||||
// - crash
|
||||
// - restart again
|
||||
type restartCrashSnapshotTest struct {
|
||||
snapshotTestBasic
|
||||
newBlocks int
|
||||
}
|
||||
|
||||
func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
||||
// It's hard to follow the test case, visualize the input
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
// fmt.Println(tt.dump())
|
||||
chain, blocks := snaptest.prepare(t)
|
||||
|
||||
// Firstly, stop the chain properly, with all snapshot journal
|
||||
// and state committed.
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfig, params.TestChainConfig, snaptest.engine, vm.Config{}, snaptest.lastAcceptedHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
newBlocks, _, _ := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.gendb, snaptest.newBlocks, 10, func(i int, b *BlockGen) {})
|
||||
newchain.InsertChain(newBlocks)
|
||||
|
||||
// Commit the entire snapshot into the disk if requested. Note only
|
||||
// (a) snapshot root and (b) snapshot generator will be committed,
|
||||
// the diff journal is not.
|
||||
for i := uint64(0); i < uint64(len(newBlocks)); i++ {
|
||||
if err := newchain.Accept(newBlocks[i]); err != nil {
|
||||
t.Fatalf("Failed to accept block %v: %v", i, err)
|
||||
}
|
||||
snaptest.lastAcceptedHash = newBlocks[i].Hash()
|
||||
}
|
||||
chain.DrainAcceptorQueue()
|
||||
|
||||
// Simulate the blockchain crash
|
||||
// Don't call chain.Stop here, so that no snapshot
|
||||
// journal and latest state will be committed
|
||||
|
||||
// Restart the chain after the crash
|
||||
newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfig, params.TestChainConfig, snaptest.engine, vm.Config{}, snaptest.lastAcceptedHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
defer newchain.Stop()
|
||||
|
||||
snaptest.verify(t, newchain, blocks)
|
||||
}
|
||||
|
||||
// wipeCrashSnapshotTest is the test type used to test this scenario:
|
||||
// - have a complete snapshot
|
||||
// - restart, insert more blocks without enabling the snapshot
|
||||
@@ -418,7 +360,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||
Pruning: true,
|
||||
CommitInterval: 4096,
|
||||
}
|
||||
newchain, err = NewBlockChain(snaptest.db, config, params.TestChainConfig, snaptest.engine, vm.Config{}, snaptest.lastAcceptedHash)
|
||||
_, err = NewBlockChain(snaptest.db, config, params.TestChainConfig, snaptest.engine, vm.Config{}, snaptest.lastAcceptedHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
||||
@@ -272,6 +272,7 @@ func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tt.testFunc(t, create)
|
||||
})
|
||||
}
|
||||
@@ -525,3 +526,117 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
|
||||
// correctly in case reorg is called.
|
||||
func TestCanonicalHashMarker(t *testing.T) {
|
||||
cases := []struct {
|
||||
forkA int
|
||||
forkB int
|
||||
}{
|
||||
// ForkA: 10 blocks
|
||||
// ForkB: 1 blocks
|
||||
//
|
||||
// reorged:
|
||||
// markers [2, 10] should be deleted
|
||||
// markers [1] should be updated
|
||||
{10, 1},
|
||||
|
||||
// ForkA: 10 blocks
|
||||
// ForkB: 2 blocks
|
||||
//
|
||||
// reorged:
|
||||
// markers [3, 10] should be deleted
|
||||
// markers [1, 2] should be updated
|
||||
{10, 2},
|
||||
|
||||
// ForkA: 10 blocks
|
||||
// ForkB: 10 blocks
|
||||
//
|
||||
// reorged:
|
||||
// markers [1, 10] should be updated
|
||||
{10, 10},
|
||||
|
||||
// ForkA: 10 blocks
|
||||
// ForkB: 11 blocks
|
||||
//
|
||||
// reorged:
|
||||
// markers [1, 11] should be updated
|
||||
{10, 11},
|
||||
}
|
||||
for _, c := range cases {
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{},
|
||||
BaseFee: big.NewInt(params.TestInitialBaseFee),
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
engine = dummy.NewFaker()
|
||||
)
|
||||
forkA, _, err := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkA, 10, func(i int, gen *BlockGen) {})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
forkB, _, err := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkB, 10, func(i int, gen *BlockGen) {})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Initialize test chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfig, params.TestChainConfig, engine, vm.Config{}, common.Hash{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
// Insert forkA and forkB, the canonical should on forkA still
|
||||
if n, err := chain.InsertChain(forkA); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
if n, err := chain.InsertChain(forkB); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
verify := func(head *types.Block) {
|
||||
if chain.CurrentBlock().Hash() != head.Hash() {
|
||||
t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash())
|
||||
}
|
||||
if chain.CurrentHeader().Hash() != head.Hash() {
|
||||
t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash())
|
||||
}
|
||||
if !chain.HasState(head.Root()) {
|
||||
t.Fatalf("Lost block state %v %x", head.Number(), head.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// Switch canonical chain to forkB if necessary
|
||||
if len(forkA) < len(forkB) {
|
||||
verify(forkB[len(forkB)-1])
|
||||
} else {
|
||||
verify(forkA[len(forkA)-1])
|
||||
if err := chain.SetPreference(forkB[len(forkB)-1]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
verify(forkB[len(forkB)-1])
|
||||
}
|
||||
|
||||
// Ensure all hash markers are updated correctly
|
||||
for i := 0; i < len(forkB); i++ {
|
||||
block := forkB[i]
|
||||
hash := chain.GetCanonicalHash(block.NumberU64())
|
||||
if hash != block.Hash() {
|
||||
t.Fatalf("Unexpected canonical hash %d", block.NumberU64())
|
||||
}
|
||||
}
|
||||
if c.forkA > c.forkB {
|
||||
for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ {
|
||||
hash := chain.GetCanonicalHash(i)
|
||||
if hash != (common.Hash{}) {
|
||||
t.Fatalf("Unexpected canonical hash %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,13 +134,13 @@ func makeRandomIndexes(lengths []int, max int) [][]bloomIndexes {
|
||||
|
||||
// testMatcherDiffBatches runs the given matches test in single-delivery and also
|
||||
// in batches delivery mode, verifying that all kinds of deliveries are handled
|
||||
// correctly withn.
|
||||
// correctly within.
|
||||
func testMatcherDiffBatches(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, intermittent bool, retrievals uint32) {
|
||||
singleton := testMatcher(t, filter, start, blocks, intermittent, retrievals, 1)
|
||||
batched := testMatcher(t, filter, start, blocks, intermittent, retrievals, 16)
|
||||
|
||||
if singleton != batched {
|
||||
t.Errorf("filter = %v blocks = %v intermittent = %v: request count mismatch, %v in signleton vs. %v in batched mode", filter, blocks, intermittent, singleton, batched)
|
||||
t.Errorf("filter = %v blocks = %v intermittent = %v: request count mismatch, %v in singleton vs. %v in batched mode", filter, blocks, intermittent, singleton, batched)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -41,7 +41,7 @@ type ChainContext interface {
|
||||
// Engine retrieves the chain's consensus engine.
|
||||
Engine() consensus.Engine
|
||||
|
||||
// GetHeader returns the hash corresponding to their hash.
|
||||
// GetHeader returns the header corresponding to the hash/number argument pair.
|
||||
GetHeader(common.Hash, uint64) *types.Header
|
||||
}
|
||||
|
||||
@@ -89,6 +89,11 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
|
||||
var cache []common.Hash
|
||||
|
||||
return func(n uint64) common.Hash {
|
||||
if ref.Number.Uint64() <= n {
|
||||
// This situation can happen if we're doing tracing and using
|
||||
// block overrides.
|
||||
return common.Hash{}
|
||||
}
|
||||
// If there's no hash cache yet, make one
|
||||
if len(cache) == 0 {
|
||||
cache = append(cache, ref.ParentHash)
|
||||
|
||||
+6
-6
@@ -56,8 +56,8 @@ import (
|
||||
//go:embed airdrops/011522.json
|
||||
var AirdropData []byte
|
||||
|
||||
//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
||||
//go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
|
||||
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
||||
//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
|
||||
|
||||
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
|
||||
|
||||
@@ -171,8 +171,8 @@ func (e *GenesisMismatchError) Error() string {
|
||||
//
|
||||
// genesis == nil genesis != nil
|
||||
// +------------------------------------------
|
||||
// db has no genesis | ErrNoGenesis | genesis
|
||||
// db has genesis | ErrNoGenesis | genesis (if compatible both block hash and chain config), else error
|
||||
// db has no genesis | main-net default | genesis
|
||||
// db has genesis | from DB | genesis (if compatible)
|
||||
|
||||
// The argument [genesis] must be specified and must contain a valid chain config.
|
||||
// If the genesis block has already been set up, then we verify the hash matches the genesis passed in
|
||||
@@ -324,7 +324,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||
if g.BaseFee != nil {
|
||||
head.BaseFee = g.BaseFee
|
||||
} else {
|
||||
head.BaseFee = g.Config.FeeConfig.MinBaseFee
|
||||
head.BaseFee = new(big.Int).Set(g.Config.FeeConfig.MinBaseFee)
|
||||
}
|
||||
}
|
||||
statedb.Commit(false, false)
|
||||
@@ -377,7 +377,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big
|
||||
g := Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{addr: {Balance: balance}},
|
||||
BaseFee: big.NewInt(params.TestMaxBaseFee.Int64()),
|
||||
BaseFee: new(big.Int).Set(params.TestMaxBaseFee),
|
||||
}
|
||||
return g.MustCommit(db)
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ type NumberHash struct {
|
||||
Hash common.Hash
|
||||
}
|
||||
|
||||
// ReadAllHashes retrieves all the hashes assigned to blocks at a certain heights,
|
||||
// both canonical and reorged forks included.
|
||||
// ReadAllHashesInRange retrieves all the hashes assigned to blocks at a certain
|
||||
// heights, both canonical and reorged forks included.
|
||||
// This method considers both limits to be _inclusive_.
|
||||
func ReadAllHashesInRange(db ethdb.Iteratee, first, last uint64) []*NumberHash {
|
||||
var (
|
||||
@@ -378,7 +378,7 @@ func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Rec
|
||||
}
|
||||
|
||||
// ReadReceipts retrieves all the transaction receipts belonging to a block, including
|
||||
// its correspoinding metadata fields. If it is unable to populate these metadata
|
||||
// its corresponding metadata fields. If it is unable to populate these metadata
|
||||
// fields then nil is returned.
|
||||
//
|
||||
// The current implementation populates these metadata fields by reading the receipts'
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -601,7 +601,7 @@ func TestDeriveLogFields(t *testing.T) {
|
||||
|
||||
func BenchmarkDecodeRLPLogs(b *testing.B) {
|
||||
// Encoded receipts from block 0x14ee094309fbe8f70b65f45ebcc08fb33f126942d97464aad5eb91cfd1e2d269
|
||||
buf, err := ioutil.ReadFile("testdata/stored_receipts.bin")
|
||||
buf, err := os.ReadFile("testdata/stored_receipts.bin")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -131,12 +131,12 @@ func DeleteStorageSnapshot(db ethdb.KeyValueWriter, accountHash, storageHash com
|
||||
// IterateStorageSnapshots returns an iterator for walking the entire storage
|
||||
// space of a specific account.
|
||||
func IterateStorageSnapshots(db ethdb.Iteratee, accountHash common.Hash) ethdb.Iterator {
|
||||
return db.NewIterator(storageSnapshotsKey(accountHash), nil)
|
||||
return NewKeyLengthIterator(db.NewIterator(storageSnapshotsKey(accountHash), nil), len(SnapshotStoragePrefix)+2*common.HashLength)
|
||||
}
|
||||
|
||||
// IterateAccountSnapshots returns an iterator for walking all of the accounts in the snapshot
|
||||
func IterateAccountSnapshots(db ethdb.Iteratee) ethdb.Iterator {
|
||||
return db.NewIterator(SnapshotAccountPrefix, nil)
|
||||
return NewKeyLengthIterator(db.NewIterator(SnapshotAccountPrefix, nil), len(SnapshotAccountPrefix)+common.HashLength)
|
||||
}
|
||||
|
||||
// ReadSnapshotGenerator retrieves the serialized snapshot generator saved at
|
||||
@@ -153,11 +153,3 @@ func WriteSnapshotGenerator(db ethdb.KeyValueWriter, generator []byte) {
|
||||
log.Crit("Failed to store snapshot generator", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteSnapshotGenerator deletes the serialized snapshot generator saved at
|
||||
// the last shutdown
|
||||
func DeleteSnapshotGenerator(db ethdb.KeyValueWriter) {
|
||||
if err := db.Delete(snapshotGeneratorKey); err != nil {
|
||||
log.Crit("Failed to remove snapshot generator", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,61 +38,13 @@ func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
|
||||
return data
|
||||
}
|
||||
|
||||
// WritePreimages writes the provided set of preimages to the database.
|
||||
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
|
||||
for hash, preimage := range preimages {
|
||||
if err := db.Put(preimageKey(hash), preimage); err != nil {
|
||||
log.Crit("Failed to store trie preimage", "err", err)
|
||||
}
|
||||
}
|
||||
preimageCounter.Inc(int64(len(preimages)))
|
||||
preimageHitCounter.Inc(int64(len(preimages)))
|
||||
}
|
||||
|
||||
// ReadCode retrieves the contract code of the provided code hash.
|
||||
func ReadCode(db ethdb.KeyValueReader, hash common.Hash) []byte {
|
||||
// Try with the prefixed code scheme first, if not then try with legacy
|
||||
// scheme.
|
||||
data := ReadCodeWithPrefix(db, hash)
|
||||
if len(data) != 0 {
|
||||
return data
|
||||
}
|
||||
// TODO: we have never used the legacy scheme, so we should be able to
|
||||
// remove the latter attempt.
|
||||
data, _ = db.Get(hash[:])
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadCodeWithPrefix retrieves the contract code of the provided code hash.
|
||||
// The main difference between this function and ReadCode is this function
|
||||
// will only check the existence with latest scheme(with prefix).
|
||||
func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
|
||||
// Try with the prefixed code scheme first and only. The legacy scheme was never used in subnet-evm.
|
||||
data, _ := db.Get(codeKey(hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// HasCodeWithPrefix checks if the contract code corresponding to the
|
||||
// provided code hash is present in the db. This function will only check
|
||||
// presence using the prefix-scheme.
|
||||
func HasCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) bool {
|
||||
ok, _ := db.Has(codeKey(hash))
|
||||
return ok
|
||||
}
|
||||
|
||||
// WriteCode writes the provided contract code database.
|
||||
func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
|
||||
if err := db.Put(codeKey(hash), code); err != nil {
|
||||
log.Crit("Failed to store contract code", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCode deletes the specified contract code from the database.
|
||||
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Delete(codeKey(hash)); err != nil {
|
||||
log.Crit("Failed to delete contract code", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTrieNode retrieves the trie node of the provided hash.
|
||||
func ReadTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
|
||||
data, _ := db.Get(hash.Bytes())
|
||||
@@ -102,7 +54,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
|
||||
// HasCode checks if the contract code corresponding to the
|
||||
// provided code hash is present in the db.
|
||||
func HasCode(db ethdb.KeyValueReader, hash common.Hash) bool {
|
||||
// Try with the prefixed code scheme first and only. The legacy scheme was never used in coreth.
|
||||
// Try with the prefixed code scheme first and only. The legacy scheme was never used in subnet-evm.
|
||||
ok, _ := db.Has(codeKey(hash))
|
||||
return ok
|
||||
}
|
||||
@@ -113,6 +65,24 @@ func HasTrieNode(db ethdb.KeyValueReader, hash common.Hash) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// WritePreimages writes the provided set of preimages to the database.
|
||||
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
|
||||
for hash, preimage := range preimages {
|
||||
if err := db.Put(preimageKey(hash), preimage); err != nil {
|
||||
log.Crit("Failed to store trie preimage", "err", err)
|
||||
}
|
||||
}
|
||||
preimageCounter.Inc(int64(len(preimages)))
|
||||
preimageHitCounter.Inc(int64(len(preimages)))
|
||||
}
|
||||
|
||||
// WriteCode writes the provided contract code database.
|
||||
func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
|
||||
if err := db.Put(codeKey(hash), code); err != nil {
|
||||
log.Crit("Failed to store contract code", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTrieNode writes the provided trie node database.
|
||||
func WriteTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
|
||||
if err := db.Put(hash.Bytes(), node); err != nil {
|
||||
@@ -120,6 +90,13 @@ func WriteTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCode deletes the specified contract code from the database.
|
||||
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Delete(codeKey(hash)); err != nil {
|
||||
log.Crit("Failed to delete contract code", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteTrieNode deletes the specified trie node from the database.
|
||||
func DeleteTrieNode(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Delete(hash.Bytes()); err != nil {
|
||||
|
||||
@@ -204,7 +204,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{
|
||||
databaseVersionKey, headHeaderKey, headBlockKey,
|
||||
snapshotRootKey, snapshotGeneratorKey, uncleanShutdownKey,
|
||||
snapshotRootKey, snapshotBlockHashKey, snapshotGeneratorKey,
|
||||
uncleanShutdownKey, syncRootKey,
|
||||
} {
|
||||
if bytes.Equal(key, meta) {
|
||||
metadata.Add(size)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKeyLengthIterator(t *testing.T) {
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
keyLen := 8
|
||||
expectedKeys := make(map[string]struct{})
|
||||
for i := 0; i < 100; i++ {
|
||||
key := make([]byte, keyLen)
|
||||
binary.BigEndian.PutUint64(key, uint64(i))
|
||||
if err := db.Put(key, []byte{0x1}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedKeys[string(key)] = struct{}{}
|
||||
|
||||
longerKey := make([]byte, keyLen*2)
|
||||
binary.BigEndian.PutUint64(longerKey, uint64(i))
|
||||
if err := db.Put(longerKey, []byte{0x1}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
it := NewKeyLengthIterator(db.NewIterator(nil, nil), keyLen)
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
_, exists := expectedKeys[string(key)]
|
||||
if !exists {
|
||||
t.Fatalf("Found unexpected key %d", binary.BigEndian.Uint64(key))
|
||||
}
|
||||
delete(expectedKeys, string(key))
|
||||
if len(key) != keyLen {
|
||||
t.Fatalf("Found unexpected key in key length iterator with length %d", len(key))
|
||||
}
|
||||
}
|
||||
|
||||
if len(expectedKeys) != 0 {
|
||||
t.Fatalf("Expected all keys of length %d to be removed from expected keys during iteration", keyLen)
|
||||
}
|
||||
}
|
||||
@@ -131,6 +131,11 @@ func (t *table) NewBatch() ethdb.Batch {
|
||||
return &tableBatch{t.db.NewBatch(), t.prefix}
|
||||
}
|
||||
|
||||
// NewBatchWithSize creates a write-only database batch with pre-allocated buffer.
|
||||
func (t *table) NewBatchWithSize(size int) ethdb.Batch {
|
||||
return &tableBatch{t.db.NewBatchWithSize(size), t.prefix}
|
||||
}
|
||||
|
||||
// tableBatch is a wrapper around a database batch that prefixes each key access
|
||||
// with a pre-configured string.
|
||||
type tableBatch struct {
|
||||
|
||||
+2
-3
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// Copyright 2020 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
|
||||
@@ -97,8 +97,7 @@ func TestRlpIterator(t *testing.T) {
|
||||
|
||||
func testRlpIterator(t *testing.T, txs, uncles, datasize int) {
|
||||
desc := fmt.Sprintf("%d txs [%d datasize] and %d uncles", txs, datasize, uncles)
|
||||
block := getBlock(txs, uncles, datasize).Body()
|
||||
bodyRlp, _ := rlp.EncodeToBytes(block)
|
||||
bodyRlp, _ := rlp.EncodeToBytes(getBlock(txs, uncles, datasize).Body())
|
||||
it, err := rlp.NewListIterator(bodyRlp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+2
-18
@@ -143,7 +143,7 @@ type cachingDB struct {
|
||||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewSecure(root, db.db)
|
||||
tr, err := trie.NewSecure(common.Hash{}, root, db.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -152,7 +152,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewSecure(root, db.db)
|
||||
tr, err := trie.NewSecure(addrHash, root, db.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -183,22 +183,6 @@ func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
|
||||
return code, nil
|
||||
}
|
||||
code := rawdb.ReadCodeWithPrefix(db.db.DiskDB(), codeHash)
|
||||
if len(code) > 0 {
|
||||
db.codeCache.Set(codeHash.Bytes(), code)
|
||||
db.codeSizeCache.Add(codeHash, len(code))
|
||||
return code, nil
|
||||
}
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
// ContractCodeSize retrieves a particular contracts code's size.
|
||||
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
|
||||
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// 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
|
||||
@@ -49,7 +49,7 @@ func (f stateBloomHasher) BlockSize() int { panic("not implem
|
||||
func (f stateBloomHasher) Size() int { return 8 }
|
||||
func (f stateBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) }
|
||||
|
||||
// stateBloom is a bloom filter used during the state convesion(snapshot->state).
|
||||
// stateBloom is a bloom filter used during the state conversion(snapshot->state).
|
||||
// The keys of all generated entries will be recorded here so that in the pruning
|
||||
// stage the entries belong to the specific version can be avoided for deletion.
|
||||
//
|
||||
@@ -100,7 +100,7 @@ func (bloom *stateBloom) Commit(filename, tempname string) error {
|
||||
return err
|
||||
}
|
||||
// Ensure the file is synced to disk
|
||||
f, err := os.OpenFile(tempname, os.O_RDWR, 0666)
|
||||
f, err := os.OpenFile(tempname, os.O_RDWR, 0o666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func (bloom *stateBloom) Commit(filename, tempname string) error {
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// Move the teporary file into it's final location
|
||||
// Move the temporary file into it's final location
|
||||
return os.Rename(tempname, filename)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// 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
|
||||
@@ -341,7 +341,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
|
||||
if genesis == nil {
|
||||
return errors.New("missing genesis block")
|
||||
}
|
||||
t, err := trie.NewSecure(genesis.Root(), trie.NewDatabase(db))
|
||||
t, err := trie.NewSecure(common.Hash{}, genesis.Root(), trie.NewDatabase(db))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -361,7 +361,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
|
||||
return err
|
||||
}
|
||||
if acc.Root != emptyRoot {
|
||||
storageTrie, err := trie.NewSecure(acc.Root, trie.NewDatabase(db))
|
||||
storageTrie, err := trie.NewSecure(common.BytesToHash(accIter.LeafKey()), acc.Root, trie.NewDatabase(db))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ type trieKV struct {
|
||||
type (
|
||||
// trieGeneratorFn is the interface of trie generation which can
|
||||
// be implemented by different trie algorithm.
|
||||
trieGeneratorFn func(db ethdb.KeyValueWriter, in chan (trieKV), out chan (common.Hash))
|
||||
trieGeneratorFn func(db ethdb.KeyValueWriter, owner common.Hash, in chan (trieKV), out chan (common.Hash))
|
||||
|
||||
// leafCallbackFn is the callback invoked at the leaves of the trie,
|
||||
// returns the subtrie root with the specified subtrie identifier.
|
||||
@@ -262,7 +262,7 @@ func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash,
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
generatorFn(db, in, out)
|
||||
generatorFn(db, account, in, out)
|
||||
}()
|
||||
// Spin up a go-routine for progress logging
|
||||
if report && stats != nil {
|
||||
@@ -369,8 +369,8 @@ func generateTrieRoot(db ethdb.KeyValueWriter, it Iterator, account common.Hash,
|
||||
return stop(nil)
|
||||
}
|
||||
|
||||
func stackTrieGenerate(db ethdb.KeyValueWriter, in chan trieKV, out chan common.Hash) {
|
||||
t := trie.NewStackTrie(db)
|
||||
func stackTrieGenerate(db ethdb.KeyValueWriter, owner common.Hash, in chan trieKV, out chan common.Hash) {
|
||||
t := trie.NewStackTrieWithOwner(db, owner)
|
||||
for leaf := range in {
|
||||
t.TryUpdate(leaf.key[:], leaf.value)
|
||||
}
|
||||
|
||||
@@ -275,6 +275,9 @@ func (dl *diffLayer) BlockHash() common.Hash {
|
||||
|
||||
// Parent returns the subsequent layer of a diff layer.
|
||||
func (dl *diffLayer) Parent() snapshot {
|
||||
dl.lock.RLock()
|
||||
defer dl.lock.RUnlock()
|
||||
|
||||
return dl.parent
|
||||
}
|
||||
|
||||
@@ -487,7 +490,6 @@ func (dl *diffLayer) flatten() snapshot {
|
||||
for storageHash, data := range storage {
|
||||
comboData[storageHash] = data
|
||||
}
|
||||
parent.storageData[accountHash] = comboData
|
||||
}
|
||||
// Return the combo parent
|
||||
return &diffLayer{
|
||||
|
||||
@@ -340,7 +340,6 @@ func BenchmarkFlatten(b *testing.B) {
|
||||
value := make([]byte, 32)
|
||||
rand.Read(value)
|
||||
accStorage[randomHash()] = value
|
||||
|
||||
}
|
||||
storage[accountKey] = accStorage
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/core/rawdb"
|
||||
"github.com/ava-labs/subnet-evm/ethdb"
|
||||
"github.com/ava-labs/subnet-evm/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
@@ -514,7 +513,8 @@ func TestDiskMidAccountPartialMerge(t *testing.T) {
|
||||
// TestDiskSeek tests that seek-operations work on the disk layer
|
||||
func TestDiskSeek(t *testing.T) {
|
||||
// Create some accounts in the disk layer
|
||||
var db ethdb.Database = rawdb.NewMemoryDatabase()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
defer db.Close()
|
||||
|
||||
// Fill even keys [0,2,4...]
|
||||
for i := 0; i < 0xff; i += 2 {
|
||||
|
||||
@@ -274,7 +274,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
|
||||
}
|
||||
}
|
||||
// Create an account and state iterator pointing to the current generator marker
|
||||
accTrie, err := trie.NewSecure(dl.root, dl.triedb)
|
||||
accTrie, err := trie.NewSecure(common.Hash{}, dl.root, dl.triedb)
|
||||
if err != nil {
|
||||
// The account trie is missing (GC), surf the chain until one becomes available
|
||||
stats.Info("Trie missing, state snapshotting paused", dl.root, dl.genMarker)
|
||||
@@ -328,7 +328,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
|
||||
// If the iterated account is a contract, iterate through corresponding contract
|
||||
// storage to generate snapshot entries.
|
||||
if acc.Root != emptyRoot {
|
||||
storeTrie, err := trie.NewSecure(acc.Root, dl.triedb)
|
||||
storeTrie, err := trie.NewSecure(accountHash, acc.Root, dl.triedb)
|
||||
if err != nil {
|
||||
log.Error("Generator failed to access storage trie", "root", dl.root, "account", accountHash, "stroot", acc.Root, "err", err)
|
||||
abort := <-dl.genAbort
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// Copyright 2019 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
|
||||
@@ -35,7 +35,6 @@ import (
|
||||
|
||||
"github.com/ava-labs/subnet-evm/core/rawdb"
|
||||
"github.com/ava-labs/subnet-evm/ethdb"
|
||||
"github.com/ava-labs/subnet-evm/ethdb/memorydb"
|
||||
"github.com/ava-labs/subnet-evm/trie"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -43,53 +42,7 @@ import (
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Tests that snapshot generation from an empty database.
|
||||
func TestGeneration(t *testing.T) {
|
||||
// We can't use statedb to make a test trie (circular dependency), so make
|
||||
// a fake one manually. We're going with a small account trie of 3 accounts,
|
||||
// two of which also has the same 3-slot storage trie attached.
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
stTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
stTrie.Update([]byte("key-1"), []byte("val-1")) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0
|
||||
stTrie.Update([]byte("key-2"), []byte("val-2")) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371
|
||||
stTrie.Update([]byte("key-3"), []byte("val-3")) // 0x51c71a47af0695957647fb68766d0becee77e953df17c29b3c2f25436f055c78
|
||||
stTrie.Commit(nil, false) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
|
||||
acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
|
||||
acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
root, _, _ := accTrie.Commit(nil, false) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
|
||||
triedb.Commit(root, false, nil)
|
||||
|
||||
if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want {
|
||||
t.Fatalf("have %#x want %#x", have, want)
|
||||
}
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
|
||||
case <-time.After(250 * time.Millisecond):
|
||||
t.Errorf("Snapshot generation failed")
|
||||
}
|
||||
checkSnapRoot(t, snap, root)
|
||||
// Signal abortion to the generator and wait for it to tear down
|
||||
stop := make(chan struct{})
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
}
|
||||
var testBlockHash = common.HexToHash("0xdeadbeef")
|
||||
|
||||
func hashData(input []byte) common.Hash {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
@@ -100,48 +53,25 @@ func hashData(input []byte) common.Hash {
|
||||
return hash
|
||||
}
|
||||
|
||||
// Tests that snapshot generation with existent flat state.
|
||||
func TestGenerateExistentState(t *testing.T) {
|
||||
// Tests that snapshot generation from an empty database.
|
||||
func TestGeneration(t *testing.T) {
|
||||
// We can't use statedb to make a test trie (circular dependency), so make
|
||||
// a fake one manually. We're going with a small account trie of 3 accounts,
|
||||
// two of which also has the same 3-slot storage trie attached.
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
stTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
stTrie.Update([]byte("key-1"), []byte("val-1")) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0
|
||||
stTrie.Update([]byte("key-2"), []byte("val-2")) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371
|
||||
stTrie.Update([]byte("key-3"), []byte("val-3")) // 0x51c71a47af0695957647fb68766d0becee77e953df17c29b3c2f25436f055c78
|
||||
stTrie.Commit(nil, false) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper := newHelper()
|
||||
stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false)
|
||||
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-1")), val)
|
||||
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-1")), hashData([]byte("key-1")), []byte("val-1"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-1")), hashData([]byte("key-2")), []byte("val-2"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-1")), hashData([]byte("key-3")), []byte("val-3"))
|
||||
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
|
||||
helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
|
||||
acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
diskdb.Put(hashData([]byte("acc-2")).Bytes(), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-2")), val)
|
||||
helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
|
||||
acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
rawdb.WriteAccountSnapshot(diskdb, hashData([]byte("acc-3")), val)
|
||||
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-3")), hashData([]byte("key-1")), []byte("val-1"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-3")), hashData([]byte("key-2")), []byte("val-2"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-3")), hashData([]byte("key-3")), []byte("val-3"))
|
||||
root, snap := helper.CommitAndGenerate() // two of which also has the same 3-slot storage trie attached.
|
||||
|
||||
root, _, _ := accTrie.Commit(nil, false) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
|
||||
triedb.Commit(root, false, nil)
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want {
|
||||
t.Fatalf("have %#x want %#x", have, want)
|
||||
}
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -150,6 +80,43 @@ func TestGenerateExistentState(t *testing.T) {
|
||||
t.Errorf("Snapshot generation failed")
|
||||
}
|
||||
checkSnapRoot(t, snap, root)
|
||||
|
||||
// Signal abortion to the generator and wait for it to tear down
|
||||
stop := make(chan struct{})
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
}
|
||||
|
||||
// Tests that snapshot generation with existent flat state.
|
||||
func TestGenerateExistentState(t *testing.T) {
|
||||
// We can't use statedb to make a test trie (circular dependency), so make
|
||||
// a fake one manually. We're going with a small account trie of 3 accounts,
|
||||
// two of which also has the same 3-slot storage trie attached.
|
||||
helper := newHelper()
|
||||
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
|
||||
|
||||
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
|
||||
case <-time.After(250 * time.Millisecond):
|
||||
t.Errorf("Snapshot generation failed")
|
||||
}
|
||||
checkSnapRoot(t, snap, root)
|
||||
|
||||
// Signal abortion to the generator and wait for it to tear down
|
||||
stop := make(chan struct{})
|
||||
snap.genAbort <- stop
|
||||
@@ -177,18 +144,21 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) {
|
||||
if snapRoot != trieRoot {
|
||||
t.Fatalf("snaproot: %#x != trieroot #%x", snapRoot, trieRoot)
|
||||
}
|
||||
if err := CheckDanglingStorage(snap.diskdb); err != nil {
|
||||
t.Fatalf("Detected dangling storages: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type testHelper struct {
|
||||
diskdb *memorydb.Database
|
||||
diskdb ethdb.Database
|
||||
triedb *trie.Database
|
||||
accTrie *trie.SecureTrie
|
||||
}
|
||||
|
||||
func newHelper() *testHelper {
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := trie.NewDatabase(diskdb)
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, common.Hash{}, triedb)
|
||||
return &testHelper{
|
||||
diskdb: diskdb,
|
||||
triedb: triedb,
|
||||
@@ -219,19 +189,29 @@ func (t *testHelper) addSnapStorage(accKey string, keys []string, vals []string)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *testHelper) makeStorageTrie(keys []string, vals []string) []byte {
|
||||
stTrie, _ := trie.NewSecure(common.Hash{}, t.triedb)
|
||||
func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []string, commit bool) []byte {
|
||||
stTrie, _ := trie.NewSecure(owner, common.Hash{}, t.triedb)
|
||||
for i, k := range keys {
|
||||
stTrie.Update([]byte(k), []byte(vals[i]))
|
||||
}
|
||||
root, _, _ := stTrie.Commit(nil, false)
|
||||
var root common.Hash
|
||||
if !commit {
|
||||
root = stTrie.Hash()
|
||||
} else {
|
||||
root, _, _ = stTrie.Commit(nil, false)
|
||||
}
|
||||
return root.Bytes()
|
||||
}
|
||||
|
||||
func (t *testHelper) Generate() (common.Hash, *diskLayer) {
|
||||
func (t *testHelper) Commit() common.Hash {
|
||||
root, _, _ := t.accTrie.Commit(nil, false)
|
||||
t.triedb.Commit(root, false, nil)
|
||||
snap := generateSnapshot(t.diskdb, t.triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
return root
|
||||
}
|
||||
|
||||
func (t *testHelper) CommitAndGenerate() (common.Hash, *diskLayer) {
|
||||
root := t.Commit()
|
||||
snap := generateSnapshot(t.diskdb, t.triedb, 16, testBlockHash, root, nil)
|
||||
return root, snap
|
||||
}
|
||||
|
||||
@@ -243,36 +223,41 @@ func (t *testHelper) Generate() (common.Hash, *diskLayer) {
|
||||
// - miss in the beginning
|
||||
// - miss in the middle
|
||||
// - miss in the end
|
||||
//
|
||||
// - the contract(non-empty storage) has wrong storage slots
|
||||
// - wrong slots in the beginning
|
||||
// - wrong slots in the middle
|
||||
// - wrong slots in the end
|
||||
//
|
||||
// - the contract(non-empty storage) has extra storage slots
|
||||
// - extra slots in the beginning
|
||||
// - extra slots in the middle
|
||||
// - extra slots in the end
|
||||
func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
|
||||
helper := newHelper()
|
||||
stRoot := helper.makeStorageTrie([]string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Account one, empty root but non-empty database
|
||||
helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Account two, non empty root but empty database
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
|
||||
// Miss slots
|
||||
{
|
||||
// Account three, non empty root but misses slots in the beginning
|
||||
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"})
|
||||
|
||||
// Account four, non empty root but misses slots in the middle
|
||||
helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"})
|
||||
|
||||
// Account five, non empty root but misses slots in the end
|
||||
helper.makeStorageTrie(hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-5", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"})
|
||||
}
|
||||
@@ -280,18 +265,22 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
|
||||
// Wrong storage slots
|
||||
{
|
||||
// Account six, non empty root but wrong slots in the beginning
|
||||
helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"})
|
||||
|
||||
// Account seven, non empty root but wrong slots in the middle
|
||||
helper.makeStorageTrie(hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-7", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"})
|
||||
|
||||
// Account eight, non empty root but wrong slots in the end
|
||||
helper.makeStorageTrie(hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-8", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"})
|
||||
|
||||
// Account 9, non empty root but rotated slots
|
||||
helper.makeStorageTrie(hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-9", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"})
|
||||
}
|
||||
@@ -299,19 +288,22 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
|
||||
// Extra storage slots
|
||||
{
|
||||
// Account 10, non empty root but extra slots in the beginning
|
||||
helper.makeStorageTrie(hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-10", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"})
|
||||
|
||||
// Account 11, non empty root but extra slots in the middle
|
||||
helper.makeStorageTrie(hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-11", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"})
|
||||
|
||||
// Account 12, non empty root but extra slots in the end
|
||||
helper.makeStorageTrie(hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addAccount("acc-12", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"})
|
||||
}
|
||||
|
||||
root, snap := helper.Generate()
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
t.Logf("Root: %#x\n", root) // Root = 0x8746cce9fd9c658b2cfd639878ed6584b7a2b3e73bb40f607fcfa156002429a0
|
||||
|
||||
select {
|
||||
@@ -335,7 +327,12 @@ func TestGenerateExistentStateWithWrongStorage(t *testing.T) {
|
||||
// - extra accounts
|
||||
func TestGenerateExistentStateWithWrongAccounts(t *testing.T) {
|
||||
helper := newHelper()
|
||||
stRoot := helper.makeStorageTrie([]string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
|
||||
// Trie accounts [acc-1, acc-2, acc-3, acc-4, acc-6]
|
||||
// Extra accounts [acc-0, acc-5, acc-7]
|
||||
@@ -363,7 +360,7 @@ func TestGenerateExistentStateWithWrongAccounts(t *testing.T) {
|
||||
helper.addSnapAccount("acc-7", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyRoot.Bytes()}) // after the end
|
||||
}
|
||||
|
||||
root, snap := helper.Generate()
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
t.Logf("Root: %#x\n", root) // Root = 0x825891472281463511e7ebcc7f109e4f9200c20fa384754e11fd605cd98464e8
|
||||
|
||||
select {
|
||||
@@ -387,29 +384,19 @@ func TestGenerateCorruptAccountTrie(t *testing.T) {
|
||||
// We can't use statedb to make a test trie (circular dependency), so make
|
||||
// a fake one manually. We're going with a small account trie of 3 accounts,
|
||||
// without any storage slots to keep the test smaller.
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
tr, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
tr.Update([]byte("acc-1"), val) // 0x7dd654835190324640832972b7c4c6eaa0c50541e36766d054ed57721f1dc7eb
|
||||
helper := newHelper()
|
||||
|
||||
acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
tr.Update([]byte("acc-2"), val) // 0xf73118e0254ce091588d66038744a0afae5f65a194de67cff310c683ae43329e
|
||||
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
|
||||
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
|
||||
|
||||
acc = &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
tr.Update([]byte("acc-3"), val) // 0x515d3de35e143cd976ad476398d910aa7bf8a02e8fd7eb9e3baacddbbcbfcb41
|
||||
tr.Commit(nil, false) // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
|
||||
root, _, _ := helper.accTrie.Commit(nil, false) // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
|
||||
|
||||
// Delete an account trie leaf and ensure the generator chokes
|
||||
triedb.Commit(common.HexToHash("0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978"), false, nil)
|
||||
diskdb.Delete(common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7").Bytes())
|
||||
helper.triedb.Commit(root, false, nil)
|
||||
helper.diskdb.Delete(common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7").Bytes())
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), common.HexToHash("0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978"), nil)
|
||||
snap := generateSnapshot(helper.diskdb, helper.triedb, 16, testBlockHash, root, nil)
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -431,47 +418,32 @@ func TestGenerateMissingStorageTrie(t *testing.T) {
|
||||
// We can't use statedb to make a test trie (circular dependency), so make
|
||||
// a fake one manually. We're going with a small account trie of 3 accounts,
|
||||
// two of which also has the same 3-slot storage trie attached.
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
stTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
stTrie.Update([]byte("key-1"), []byte("val-1")) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0
|
||||
stTrie.Update([]byte("key-2"), []byte("val-2")) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371
|
||||
stTrie.Update([]byte("key-3"), []byte("val-3")) // 0x51c71a47af0695957647fb68766d0becee77e953df17c29b3c2f25436f055c78
|
||||
stTrie.Commit(nil, false) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper := newHelper()
|
||||
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
|
||||
acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
|
||||
acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
accTrie.Commit(nil, false) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
root, _, _ := helper.accTrie.Commit(nil, false)
|
||||
|
||||
// We can only corrupt the disk database, so flush the tries out
|
||||
triedb.Reference(
|
||||
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
|
||||
helper.triedb.Reference(
|
||||
common.BytesToHash(stRoot),
|
||||
common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"),
|
||||
true,
|
||||
)
|
||||
triedb.Reference(
|
||||
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
|
||||
helper.triedb.Reference(
|
||||
common.BytesToHash(stRoot),
|
||||
common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"),
|
||||
true,
|
||||
)
|
||||
triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil)
|
||||
helper.triedb.Commit(root, false, nil)
|
||||
|
||||
// Delete a storage trie root and ensure the generator chokes
|
||||
diskdb.Delete(common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67").Bytes())
|
||||
helper.diskdb.Delete(stRoot) // We can only corrupt the disk database, so flush the tries out
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), nil)
|
||||
snap := generateSnapshot(helper.diskdb, helper.triedb, 16, testBlockHash, root, nil)
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -492,47 +464,33 @@ func TestGenerateCorruptStorageTrie(t *testing.T) {
|
||||
// We can't use statedb to make a test trie (circular dependency), so make
|
||||
// a fake one manually. We're going with a small account trie of 3 accounts,
|
||||
// two of which also has the same 3-slot storage trie attached.
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
stTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
stTrie.Update([]byte("key-1"), []byte("val-1")) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0
|
||||
stTrie.Update([]byte("key-2"), []byte("val-2")) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371
|
||||
stTrie.Update([]byte("key-3"), []byte("val-3")) // 0x51c71a47af0695957647fb68766d0becee77e953df17c29b3c2f25436f055c78
|
||||
stTrie.Commit(nil, false) // Root: 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper := newHelper()
|
||||
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x1314700b81afc49f94db3623ef1df38f3ed18b73a1b7ea2f6c095118cf6118a0
|
||||
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371
|
||||
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
|
||||
acc = &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-2"), val) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
|
||||
acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ = rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
accTrie.Commit(nil, false) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
|
||||
root, _, _ := helper.accTrie.Commit(nil, false)
|
||||
|
||||
// We can only corrupt the disk database, so flush the tries out
|
||||
triedb.Reference(
|
||||
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
|
||||
helper.triedb.Reference(
|
||||
common.BytesToHash(stRoot),
|
||||
common.HexToHash("0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e"),
|
||||
true,
|
||||
)
|
||||
triedb.Reference(
|
||||
common.HexToHash("0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67"),
|
||||
helper.triedb.Reference(
|
||||
common.BytesToHash(stRoot),
|
||||
common.HexToHash("0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2"),
|
||||
true,
|
||||
)
|
||||
triedb.Commit(common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), false, nil)
|
||||
helper.triedb.Commit(root, false, nil)
|
||||
|
||||
// Delete a storage trie leaf and ensure the generator chokes
|
||||
diskdb.Delete(common.HexToHash("0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371").Bytes())
|
||||
helper.diskdb.Delete(common.HexToHash("0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371").Bytes())
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"), nil)
|
||||
snap := generateSnapshot(helper.diskdb, helper.triedb, 16, testBlockHash, root, nil)
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -547,56 +505,51 @@ func TestGenerateCorruptStorageTrie(t *testing.T) {
|
||||
<-stop
|
||||
}
|
||||
|
||||
func getStorageTrie(n int, triedb *trie.Database) *trie.SecureTrie {
|
||||
stTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
for i := 0; i < n; i++ {
|
||||
k := fmt.Sprintf("key-%d", i)
|
||||
v := fmt.Sprintf("val-%d", i)
|
||||
stTrie.Update([]byte(k), []byte(v))
|
||||
}
|
||||
stTrie.Commit(nil, false)
|
||||
return stTrie
|
||||
}
|
||||
|
||||
// Tests that snapshot generation when an extra account with storage exists in the snap state.
|
||||
func TestGenerateWithExtraAccounts(t *testing.T) {
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
stTrie = getStorageTrie(5, triedb)
|
||||
)
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
{ // Account one in the trie
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
helper := newHelper()
|
||||
{
|
||||
// Account one in the trie
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")),
|
||||
[]string{"key-1", "key-2", "key-3", "key-4", "key-5"},
|
||||
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"},
|
||||
true,
|
||||
)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
|
||||
// Identical in the snap
|
||||
key := hashData([]byte("acc-1"))
|
||||
rawdb.WriteAccountSnapshot(diskdb, key, val)
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-1")), []byte("val-1"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-2")), []byte("val-2"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-3")), []byte("val-3"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-4")), []byte("val-4"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-5")), []byte("val-5"))
|
||||
rawdb.WriteAccountSnapshot(helper.triedb.DiskDB(), key, val)
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-1")), []byte("val-1"))
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-2")), []byte("val-2"))
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-3")), []byte("val-3"))
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-4")), []byte("val-4"))
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-5")), []byte("val-5"))
|
||||
}
|
||||
{ // Account two exists only in the snapshot
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
{
|
||||
// Account two exists only in the snapshot
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")),
|
||||
[]string{"key-1", "key-2", "key-3", "key-4", "key-5"},
|
||||
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"},
|
||||
true,
|
||||
)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
key := hashData([]byte("acc-2"))
|
||||
rawdb.WriteAccountSnapshot(diskdb, key, val)
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("b-key-1")), []byte("b-val-1"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("b-key-2")), []byte("b-val-2"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("b-key-3")), []byte("b-val-3"))
|
||||
rawdb.WriteAccountSnapshot(helper.triedb.DiskDB(), key, val)
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-1")), []byte("b-val-1"))
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-2")), []byte("b-val-2"))
|
||||
rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-3")), []byte("b-val-3"))
|
||||
}
|
||||
root, _, _ := accTrie.Commit(nil, false)
|
||||
t.Logf("root: %x", root)
|
||||
triedb.Commit(root, false, nil)
|
||||
root := helper.Commit()
|
||||
|
||||
// To verify the test: If we now inspect the snap db, there should exist extraneous storage items
|
||||
if data := rawdb.ReadStorageSnapshot(diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data == nil {
|
||||
if data := rawdb.ReadStorageSnapshot(helper.triedb.DiskDB(), hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data == nil {
|
||||
t.Fatalf("expected snap storage to exist")
|
||||
}
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
snap := generateSnapshot(helper.diskdb, helper.triedb, 16, testBlockHash, root, nil)
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -605,12 +558,13 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
|
||||
t.Errorf("Snapshot generation failed")
|
||||
}
|
||||
checkSnapRoot(t, snap, root)
|
||||
|
||||
// Signal abortion to the generator and wait for it to tear down
|
||||
stop := make(chan struct{})
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
// If we now inspect the snap db, there should exist no extraneous storage items
|
||||
if data := rawdb.ReadStorageSnapshot(diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil {
|
||||
if data := rawdb.ReadStorageSnapshot(helper.triedb.DiskDB(), hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil {
|
||||
t.Fatalf("expected slot to be removed, got %v", string(data))
|
||||
}
|
||||
}
|
||||
@@ -624,37 +578,36 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) {
|
||||
if false {
|
||||
enableLogging()
|
||||
}
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
stTrie = getStorageTrie(3, triedb)
|
||||
)
|
||||
accTrie, _ := trie.NewSecure(common.Hash{}, triedb)
|
||||
{ // Account one in the trie
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
helper := newHelper()
|
||||
{
|
||||
// Account one in the trie
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")),
|
||||
[]string{"key-1", "key-2", "key-3"},
|
||||
[]string{"val-1", "val-2", "val-3"},
|
||||
true,
|
||||
)
|
||||
acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
|
||||
// Identical in the snap
|
||||
key := hashData([]byte("acc-1"))
|
||||
rawdb.WriteAccountSnapshot(diskdb, key, val)
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-1")), []byte("val-1"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-2")), []byte("val-2"))
|
||||
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("key-3")), []byte("val-3"))
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
|
||||
rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-1")), []byte("val-1"))
|
||||
rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-2")), []byte("val-2"))
|
||||
rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-3")), []byte("val-3"))
|
||||
}
|
||||
{ // 100 accounts exist only in snapshot
|
||||
{
|
||||
// 100 accounts exist only in snapshot
|
||||
for i := 0; i < 1000; i++ {
|
||||
// acc := &Account{Balance: big.NewInt(int64(i)), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
key := hashData([]byte(fmt.Sprintf("acc-%d", i)))
|
||||
rawdb.WriteAccountSnapshot(diskdb, key, val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
|
||||
}
|
||||
}
|
||||
root, _, _ := accTrie.Commit(nil, false)
|
||||
t.Logf("root: %x", root)
|
||||
triedb.Commit(root, false, nil)
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -682,31 +635,22 @@ func TestGenerateWithExtraBeforeAndAfter(t *testing.T) {
|
||||
if false {
|
||||
enableLogging()
|
||||
}
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
accTrie, _ := trie.New(common.Hash{}, triedb)
|
||||
helper := newHelper()
|
||||
{
|
||||
acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update(common.HexToHash("0x03").Bytes(), val)
|
||||
accTrie.Update(common.HexToHash("0x07").Bytes(), val)
|
||||
helper.accTrie.Update(common.HexToHash("0x03").Bytes(), val)
|
||||
helper.accTrie.Update(common.HexToHash("0x07").Bytes(), val)
|
||||
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x01"), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x02"), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x03"), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x04"), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x05"), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x06"), val)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x07"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x01"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x02"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x03"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x04"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x05"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x06"), val)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x07"), val)
|
||||
}
|
||||
|
||||
root, _, _ := accTrie.Commit(nil, false)
|
||||
t.Logf("root: %x", root)
|
||||
triedb.Commit(root, false, nil)
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -727,29 +671,20 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) {
|
||||
if false {
|
||||
enableLogging()
|
||||
}
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = trie.NewDatabase(diskdb)
|
||||
)
|
||||
accTrie, _ := trie.New(common.Hash{}, triedb)
|
||||
helper := newHelper()
|
||||
{
|
||||
acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
accTrie.Update(common.HexToHash("0x03").Bytes(), val)
|
||||
helper.accTrie.Update(common.HexToHash("0x03").Bytes(), val)
|
||||
|
||||
junk := make([]byte, 100)
|
||||
copy(junk, []byte{0xde, 0xad})
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x02"), junk)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x03"), junk)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x04"), junk)
|
||||
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x05"), junk)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x02"), junk)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x03"), junk)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x04"), junk)
|
||||
rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x05"), junk)
|
||||
}
|
||||
|
||||
root, _, _ := accTrie.Commit(nil, false)
|
||||
t.Logf("root: %x", root)
|
||||
triedb.Commit(root, false, nil)
|
||||
|
||||
snap := generateSnapshot(diskdb, triedb, 16, common.HexToHash("0xdeadbeef"), root, nil)
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
@@ -763,7 +698,7 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) {
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
// If we now inspect the snap db, there should exist no extraneous storage items
|
||||
if data := rawdb.ReadStorageSnapshot(diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil {
|
||||
if data := rawdb.ReadStorageSnapshot(helper.diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil {
|
||||
t.Fatalf("expected slot to be removed, got %v", string(data))
|
||||
}
|
||||
}
|
||||
@@ -771,13 +706,13 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) {
|
||||
func TestGenerateFromEmptySnap(t *testing.T) {
|
||||
// enableLogging()
|
||||
helper := newHelper()
|
||||
stRoot := helper.makeStorageTrie([]string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
// Add 1K accounts to the trie
|
||||
for i := 0; i < 400; i++ {
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addTrieAccount(fmt.Sprintf("acc-%d", i),
|
||||
&Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
}
|
||||
root, snap := helper.Generate()
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4
|
||||
|
||||
select {
|
||||
@@ -805,12 +740,12 @@ func TestGenerateWithIncompleteStorage(t *testing.T) {
|
||||
helper := newHelper()
|
||||
stKeys := []string{"1", "2", "3", "4", "5", "6", "7", "8"}
|
||||
stVals := []string{"v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8"}
|
||||
stRoot := helper.makeStorageTrie(stKeys, stVals)
|
||||
// We add 8 accounts, each one is missing exactly one of the storage slots. This means
|
||||
// we don't have to order the keys and figure out exactly which hash-key winds up
|
||||
// on the sensitive spots at the boundaries
|
||||
for i := 0; i < 8; i++ {
|
||||
accKey := fmt.Sprintf("acc-%d", i)
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte(accKey)), stKeys, stVals, true)
|
||||
helper.addAccount(accKey, &Account{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
var moddedKeys []string
|
||||
var moddedVals []string
|
||||
@@ -822,8 +757,7 @@ func TestGenerateWithIncompleteStorage(t *testing.T) {
|
||||
}
|
||||
helper.addSnapStorage(accKey, moddedKeys, moddedVals)
|
||||
}
|
||||
|
||||
root, snap := helper.Generate()
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
t.Logf("Root: %#x\n", root) // Root: 0xca73f6f05ba4ca3024ef340ef3dfca8fdabc1b677ff13f5a9571fd49c16e67ff
|
||||
|
||||
select {
|
||||
@@ -839,3 +773,122 @@ func TestGenerateWithIncompleteStorage(t *testing.T) {
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
}
|
||||
|
||||
func incKey(key []byte) []byte {
|
||||
for i := len(key) - 1; i >= 0; i-- {
|
||||
key[i]++
|
||||
if key[i] != 0x0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func decKey(key []byte) []byte {
|
||||
for i := len(key) - 1; i >= 0; i-- {
|
||||
key[i]--
|
||||
if key[i] != 0xff {
|
||||
break
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func populateDangling(disk ethdb.KeyValueStore) {
|
||||
populate := func(accountHash common.Hash, keys []string, vals []string) {
|
||||
for i, key := range keys {
|
||||
rawdb.WriteStorageSnapshot(disk, accountHash, hashData([]byte(key)), []byte(vals[i]))
|
||||
}
|
||||
}
|
||||
// Dangling storages of the "first" account
|
||||
populate(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Dangling storages of the "last" account
|
||||
populate(common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Dangling storages around the account 1
|
||||
hash := decKey(hashData([]byte("acc-1")).Bytes())
|
||||
populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
hash = incKey(hashData([]byte("acc-1")).Bytes())
|
||||
populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Dangling storages around the account 2
|
||||
hash = decKey(hashData([]byte("acc-2")).Bytes())
|
||||
populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
hash = incKey(hashData([]byte("acc-2")).Bytes())
|
||||
populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Dangling storages around the account 3
|
||||
hash = decKey(hashData([]byte("acc-3")).Bytes())
|
||||
populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
hash = incKey(hashData([]byte("acc-3")).Bytes())
|
||||
populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
// Dangling storages of the random account
|
||||
populate(randomHash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
populate(randomHash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
populate(randomHash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
}
|
||||
|
||||
// Tests that snapshot generation with dangling storages. Dangling storage means
|
||||
// the storage data is existent while the corresponding account data is missing.
|
||||
//
|
||||
// This test will populate some dangling storages to see if they can be cleaned up.
|
||||
func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) {
|
||||
helper := newHelper()
|
||||
stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
|
||||
helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
|
||||
helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
|
||||
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
|
||||
|
||||
populateDangling(helper.diskdb)
|
||||
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Errorf("Snapshot generation failed")
|
||||
}
|
||||
checkSnapRoot(t, snap, root)
|
||||
|
||||
// Signal abortion to the generator and wait for it to tear down
|
||||
stop := make(chan struct{})
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
}
|
||||
|
||||
// Tests that snapshot generation with dangling storages. Dangling storage means
|
||||
// the storage data is existent while the corresponding account data is missing.
|
||||
//
|
||||
// This test will populate some dangling storages to see if they can be cleaned up.
|
||||
func TestGenerateBrokenSnapshotWithDanglingStorage(t *testing.T) {
|
||||
helper := newHelper()
|
||||
stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
|
||||
helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()})
|
||||
helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()})
|
||||
|
||||
populateDangling(helper.diskdb)
|
||||
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
select {
|
||||
case <-snap.genPending:
|
||||
// Snapshot generation succeeded
|
||||
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Errorf("Snapshot generation failed")
|
||||
}
|
||||
checkSnapRoot(t, snap, root)
|
||||
|
||||
// Signal abortion to the generator and wait for it to tear down
|
||||
stop := make(chan struct{})
|
||||
snap.genAbort <- stop
|
||||
<-stop
|
||||
}
|
||||
|
||||
@@ -351,10 +351,16 @@ type diskStorageIterator struct {
|
||||
// is always false.
|
||||
func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) {
|
||||
pos := common.TrimRightZeroes(seek[:])
|
||||
|
||||
// create prefix to be rawdb.SnapshotStoragePrefix + account[:]
|
||||
prefix := make([]byte, len(rawdb.SnapshotStoragePrefix)+common.HashLength)
|
||||
copy(prefix, rawdb.SnapshotStoragePrefix)
|
||||
copy(prefix[len(rawdb.SnapshotStoragePrefix):], account[:])
|
||||
|
||||
return &diskStorageIterator{
|
||||
layer: dl,
|
||||
account: account,
|
||||
it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos),
|
||||
it: dl.diskdb.NewIterator(prefix, pos),
|
||||
}, false
|
||||
}
|
||||
|
||||
|
||||
@@ -165,12 +165,6 @@ type snapshot interface {
|
||||
// Stale return whether this layer has become stale (was flattened across) or
|
||||
// if it's still live.
|
||||
Stale() bool
|
||||
|
||||
// AccountIterator creates an account iterator over an arbitrary layer.
|
||||
AccountIterator(seek common.Hash) AccountIterator
|
||||
|
||||
// StorageIterator creates a storage iterator over an arbitrary layer.
|
||||
StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool)
|
||||
}
|
||||
|
||||
// Tree is an Ethereum state snapshot tree. It consists of one persistent base
|
||||
@@ -622,20 +616,19 @@ func diffToDisk(bottom *diffLayer) (*diskLayer, bool, error) {
|
||||
|
||||
it := rawdb.IterateStorageSnapshots(base.diskdb, hash)
|
||||
for it.Next() {
|
||||
if key := it.Key(); len(key) == 65 { // TODO(karalabe): Yuck, we should move this into the iterator
|
||||
batch.Delete(key)
|
||||
base.cache.Del(key[1:])
|
||||
snapshotFlushStorageItemMeter.Mark(1)
|
||||
key := it.Key()
|
||||
batch.Delete(key)
|
||||
base.cache.Del(key[1:])
|
||||
snapshotFlushStorageItemMeter.Mark(1)
|
||||
|
||||
// Ensure we don't delete too much data blindly (contract can be
|
||||
// huge). It's ok to flush, the root will go missing in case of a
|
||||
// crash and we'll detect and regenerate the snapshot.
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to write storage deletions", "err", err)
|
||||
}
|
||||
batch.Reset()
|
||||
// Ensure we don't delete too much data blindly (contract can be
|
||||
// huge). It's ok to flush, the root will go missing in case of a
|
||||
// crash and we'll detect and regenerate the snapshot.
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to write storage deletions", "err", err)
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
}
|
||||
it.Release()
|
||||
@@ -759,7 +752,6 @@ func (t *Tree) Rebuild(blockHash, root common.Hash) {
|
||||
if stats := layer.genStats; stats != nil {
|
||||
wiper = stats.wiping
|
||||
}
|
||||
|
||||
}
|
||||
// Layer should be inactive now, mark it as stale
|
||||
layer.lock.Lock()
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// (c) 2022, Ava Labs, Inc.
|
||||
//
|
||||
// This file is a derived work, based on the go-ethereum library whose original
|
||||
// notices appear below.
|
||||
//
|
||||
// It is distributed under a license compatible with the licensing terms of the
|
||||
// original code from which it is derived.
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2022 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 snapshot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/core/rawdb"
|
||||
"github.com/ava-labs/subnet-evm/ethdb"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// CheckDanglingStorage iterates the snap storage data, and verifies that all
|
||||
// storage also has corresponding account data.
|
||||
func CheckDanglingStorage(chaindb ethdb.KeyValueStore) error {
|
||||
if err := checkDanglingDiskStorage(chaindb); err != nil {
|
||||
log.Error("Database check error", "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkDanglingDiskStorage checks if there is any 'dangling' storage data in the
|
||||
// disk-backed snapshot layer.
|
||||
func checkDanglingDiskStorage(chaindb ethdb.KeyValueStore) error {
|
||||
var (
|
||||
lastReport = time.Now()
|
||||
start = time.Now()
|
||||
lastKey []byte
|
||||
it = rawdb.NewKeyLengthIterator(chaindb.NewIterator(rawdb.SnapshotStoragePrefix, nil), 1+2*common.HashLength)
|
||||
)
|
||||
log.Info("Checking dangling snapshot disk storage")
|
||||
|
||||
defer it.Release()
|
||||
for it.Next() {
|
||||
k := it.Key()
|
||||
accKey := k[1:33]
|
||||
if bytes.Equal(accKey, lastKey) {
|
||||
// No need to look up for every slot
|
||||
continue
|
||||
}
|
||||
lastKey = common.CopyBytes(accKey)
|
||||
if time.Since(lastReport) > time.Second*8 {
|
||||
log.Info("Iterating snap storage", "at", fmt.Sprintf("%#x", accKey), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
lastReport = time.Now()
|
||||
}
|
||||
if data := rawdb.ReadAccountSnapshot(chaindb, common.BytesToHash(accKey)); len(data) == 0 {
|
||||
log.Warn("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accKey), "storagekey", fmt.Sprintf("%#x", k))
|
||||
return fmt.Errorf("dangling snapshot storage account %#x", accKey)
|
||||
}
|
||||
}
|
||||
log.Info("Verified the snapshot disk storage", "time", common.PrettyDuration(time.Since(start)), "err", it.Error())
|
||||
return nil
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (s Storage) String() (str string) {
|
||||
}
|
||||
|
||||
func (s Storage) Copy() Storage {
|
||||
cpy := make(Storage)
|
||||
cpy := make(Storage, len(s))
|
||||
for key, value := range s {
|
||||
cpy[key] = value
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func (s *stateObject) getTrie(db Database) Trie {
|
||||
if s.data.Root != emptyRoot && s.db.prefetcher != nil {
|
||||
// When the miner is creating the pending state, there is no
|
||||
// prefetcher
|
||||
s.trie = s.db.prefetcher.trie(s.data.Root)
|
||||
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
|
||||
}
|
||||
if s.trie == nil {
|
||||
var err error
|
||||
@@ -310,7 +310,7 @@ func (s *stateObject) finalise(prefetch bool) {
|
||||
}
|
||||
}
|
||||
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot {
|
||||
s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch)
|
||||
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, slotsToPrefetch)
|
||||
}
|
||||
if len(s.dirtyStorage) > 0 {
|
||||
s.dirtyStorage = make(Storage)
|
||||
@@ -367,7 +367,7 @@ func (s *stateObject) updateTrie(db Database) Trie {
|
||||
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
|
||||
}
|
||||
if s.db.prefetcher != nil {
|
||||
s.db.prefetcher.used(s.data.Root, usedStorage)
|
||||
s.db.prefetcher.used(s.addrHash, s.data.Root, usedStorage)
|
||||
}
|
||||
if len(s.pendingStorage) > 0 {
|
||||
s.pendingStorage = make(Storage)
|
||||
|
||||
+17
-10
@@ -70,11 +70,14 @@ func (n *proofList) Delete(key []byte) error {
|
||||
// * Contracts
|
||||
// * Accounts
|
||||
type StateDB struct {
|
||||
db Database
|
||||
prefetcher *triePrefetcher
|
||||
originalRoot common.Hash // The pre-state root, before any changes were made
|
||||
trie Trie
|
||||
hasher crypto.KeccakState
|
||||
db Database
|
||||
prefetcher *triePrefetcher
|
||||
trie Trie
|
||||
hasher crypto.KeccakState
|
||||
|
||||
// originalRoot is the pre-state root, before any changes were made.
|
||||
// It will be updated when the Commit is called.
|
||||
originalRoot common.Hash
|
||||
|
||||
snap snapshot.Snapshot
|
||||
snapDestructs map[common.Hash]struct{}
|
||||
@@ -670,6 +673,7 @@ func (s *StateDB) Copy() *StateDB {
|
||||
state := &StateDB{
|
||||
db: s.db,
|
||||
trie: s.db.CopyTrie(s.trie),
|
||||
originalRoot: s.originalRoot,
|
||||
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)),
|
||||
@@ -831,7 +835,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||
addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure
|
||||
}
|
||||
if s.prefetcher != nil && len(addressesToPrefetch) > 0 {
|
||||
s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch)
|
||||
s.prefetcher.prefetch(common.Hash{}, s.originalRoot, addressesToPrefetch)
|
||||
}
|
||||
// Invalidate journal because reverting across transactions is not allowed.
|
||||
s.clearJournalAndRefund()
|
||||
@@ -872,7 +876,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
// _untouched_. We can check with the prefetcher, if it can give us a trie
|
||||
// which has the same root, but also has some content loaded into it.
|
||||
if prefetcher != nil {
|
||||
if trie := prefetcher.trie(s.originalRoot); trie != nil {
|
||||
if trie := prefetcher.trie(common.Hash{}, s.originalRoot); trie != nil {
|
||||
s.trie = trie
|
||||
}
|
||||
}
|
||||
@@ -888,7 +892,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
|
||||
}
|
||||
if prefetcher != nil {
|
||||
prefetcher.used(s.originalRoot, usedAddrs)
|
||||
prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs)
|
||||
}
|
||||
if len(s.stateObjectsPending) > 0 {
|
||||
s.stateObjectsPending = make(map[common.Address]struct{})
|
||||
@@ -905,7 +909,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
func (s *StateDB) Prepare(thash common.Hash, ti int) {
|
||||
s.thash = thash
|
||||
s.txIndex = ti
|
||||
s.accessList = newAccessList()
|
||||
}
|
||||
|
||||
func (s *StateDB) clearJournalAndRefund() {
|
||||
@@ -969,7 +972,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas
|
||||
// The onleaf func is called _serially_, so we can reuse the same account
|
||||
// for unmarshalling every time.
|
||||
var account types.StateAccount
|
||||
root, accountCommitted, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
|
||||
root, accountCommitted, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash, _ []byte) error {
|
||||
if err := rlp.DecodeBytes(leaf, &account); err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -1007,6 +1010,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas
|
||||
s.snap, s.snapDestructs, s.snapAccounts, s.snapStorage = nil, nil, nil, nil
|
||||
}
|
||||
|
||||
s.originalRoot = root
|
||||
return root, err
|
||||
}
|
||||
|
||||
@@ -1020,6 +1024,9 @@ func (s *StateDB) commit(deleteEmptyObjects bool, snaps *snapshot.Tree, blockHas
|
||||
//
|
||||
// This method should only be called if Berlin/SubnetEVM/2929+2930 is applicable at the current number.
|
||||
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
||||
// Clear out any leftover from previous executions
|
||||
s.accessList = newAccessList()
|
||||
|
||||
s.AddAddressToAccessList(sender)
|
||||
if dst != nil {
|
||||
s.AddAddressToAccessList(*dst)
|
||||
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// triePrefetchMetricsPrefix is the prefix under which to publis the metrics.
|
||||
// triePrefetchMetricsPrefix is the prefix under which to publish the metrics.
|
||||
var triePrefetchMetricsPrefix = "trie/prefetch/"
|
||||
|
||||
// triePrefetcher is an active prefetcher, which receives accounts or storage
|
||||
@@ -43,10 +43,10 @@ var triePrefetchMetricsPrefix = "trie/prefetch/"
|
||||
//
|
||||
// Note, the prefetcher's API is not thread safe.
|
||||
type triePrefetcher struct {
|
||||
db Database // Database to fetch trie nodes through
|
||||
root common.Hash // Root hash of theaccount trie for metrics
|
||||
fetches map[common.Hash]Trie // Partially or fully fetcher tries
|
||||
fetchers map[common.Hash]*subfetcher // Subfetchers for each trie
|
||||
db Database // Database to fetch trie nodes through
|
||||
root common.Hash // Root hash of the account trie for metrics
|
||||
fetches map[string]Trie // Partially or fully fetcher tries
|
||||
fetchers map[string]*subfetcher // Subfetchers for each trie
|
||||
|
||||
deliveryMissMeter metrics.Meter
|
||||
accountLoadMeter metrics.Meter
|
||||
@@ -59,13 +59,12 @@ type triePrefetcher struct {
|
||||
storageWasteMeter metrics.Meter
|
||||
}
|
||||
|
||||
// newTriePrefetcher
|
||||
func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePrefetcher {
|
||||
prefix := triePrefetchMetricsPrefix + namespace
|
||||
p := &triePrefetcher{
|
||||
db: db,
|
||||
root: root,
|
||||
fetchers: make(map[common.Hash]*subfetcher), // Active prefetchers use the fetchers map
|
||||
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
|
||||
|
||||
deliveryMissMeter: metrics.GetOrRegisterMeter(prefix+"/deliverymiss", nil),
|
||||
accountLoadMeter: metrics.GetOrRegisterMeter(prefix+"/account/load", nil),
|
||||
@@ -120,7 +119,7 @@ func (p *triePrefetcher) copy() *triePrefetcher {
|
||||
copy := &triePrefetcher{
|
||||
db: p.db,
|
||||
root: p.root,
|
||||
fetches: make(map[common.Hash]Trie), // Active prefetchers use the fetches map
|
||||
fetches: make(map[string]Trie), // Active prefetchers use the fetches map
|
||||
|
||||
deliveryMissMeter: p.deliveryMissMeter,
|
||||
accountLoadMeter: p.accountLoadMeter,
|
||||
@@ -140,33 +139,35 @@ func (p *triePrefetcher) copy() *triePrefetcher {
|
||||
return copy
|
||||
}
|
||||
// Otherwise we're copying an active fetcher, retrieve the current states
|
||||
for root, fetcher := range p.fetchers {
|
||||
copy.fetches[root] = fetcher.peek()
|
||||
for id, fetcher := range p.fetchers {
|
||||
copy.fetches[id] = fetcher.peek()
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
// prefetch schedules a batch of trie items to prefetch.
|
||||
func (p *triePrefetcher) prefetch(root common.Hash, keys [][]byte) {
|
||||
func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, keys [][]byte) {
|
||||
// If the prefetcher is an inactive one, bail out
|
||||
if p.fetches != nil {
|
||||
return
|
||||
}
|
||||
// Active fetcher, schedule the retrievals
|
||||
fetcher := p.fetchers[root]
|
||||
id := p.trieID(owner, root)
|
||||
fetcher := p.fetchers[id]
|
||||
if fetcher == nil {
|
||||
fetcher = newSubfetcher(p.db, root)
|
||||
p.fetchers[root] = fetcher
|
||||
fetcher = newSubfetcher(p.db, owner, root)
|
||||
p.fetchers[id] = fetcher
|
||||
}
|
||||
fetcher.schedule(keys)
|
||||
}
|
||||
|
||||
// trie returns the trie matching the root hash, or nil if the prefetcher doesn't
|
||||
// have it.
|
||||
func (p *triePrefetcher) trie(root common.Hash) Trie {
|
||||
func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie {
|
||||
// If the prefetcher is inactive, return from existing deep copies
|
||||
id := p.trieID(owner, root)
|
||||
if p.fetches != nil {
|
||||
trie := p.fetches[root]
|
||||
trie := p.fetches[id]
|
||||
if trie == nil {
|
||||
p.deliveryMissMeter.Mark(1)
|
||||
return nil
|
||||
@@ -174,7 +175,7 @@ func (p *triePrefetcher) trie(root common.Hash) Trie {
|
||||
return p.db.CopyTrie(trie)
|
||||
}
|
||||
// Otherwise the prefetcher is active, bail if no trie was prefetched for this root
|
||||
fetcher := p.fetchers[root]
|
||||
fetcher := p.fetchers[id]
|
||||
if fetcher == nil {
|
||||
p.deliveryMissMeter.Mark(1)
|
||||
return nil
|
||||
@@ -193,20 +194,26 @@ func (p *triePrefetcher) trie(root common.Hash) Trie {
|
||||
|
||||
// used marks a batch of state items used to allow creating statistics as to
|
||||
// how useful or wasteful the prefetcher is.
|
||||
func (p *triePrefetcher) used(root common.Hash, used [][]byte) {
|
||||
if fetcher := p.fetchers[root]; fetcher != nil {
|
||||
func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) {
|
||||
if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil {
|
||||
fetcher.used = used
|
||||
}
|
||||
}
|
||||
|
||||
// trieID returns an unique trie identifier consists the trie owner and root hash.
|
||||
func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
|
||||
return string(append(owner.Bytes(), root.Bytes()...))
|
||||
}
|
||||
|
||||
// subfetcher is a trie fetcher goroutine responsible for pulling entries for a
|
||||
// single trie. It is spawned when a new root is encountered and lives until the
|
||||
// main prefetcher is paused and either all requested items are processed or if
|
||||
// the trie being worked on is retrieved from the prefetcher.
|
||||
type subfetcher struct {
|
||||
db Database // Database to load trie nodes through
|
||||
root common.Hash // Root hash of the trie to prefetch
|
||||
trie Trie // Trie being populated with nodes
|
||||
db Database // Database to load trie nodes through
|
||||
owner common.Hash // Owner of the trie, usually account hash
|
||||
root common.Hash // Root hash of the trie to prefetch
|
||||
trie Trie // Trie being populated with nodes
|
||||
|
||||
tasks [][]byte // Items queued up for retrieval
|
||||
lock sync.Mutex // Lock protecting the task queue
|
||||
@@ -223,15 +230,16 @@ type subfetcher struct {
|
||||
|
||||
// newSubfetcher creates a goroutine to prefetch state items belonging to a
|
||||
// particular root hash.
|
||||
func newSubfetcher(db Database, root common.Hash) *subfetcher {
|
||||
func newSubfetcher(db Database, owner common.Hash, root common.Hash) *subfetcher {
|
||||
sf := &subfetcher{
|
||||
db: db,
|
||||
root: root,
|
||||
wake: make(chan struct{}, 1),
|
||||
stop: make(chan struct{}),
|
||||
term: make(chan struct{}),
|
||||
copy: make(chan chan Trie),
|
||||
seen: make(map[string]struct{}),
|
||||
db: db,
|
||||
owner: owner,
|
||||
root: root,
|
||||
wake: make(chan struct{}, 1),
|
||||
stop: make(chan struct{}),
|
||||
term: make(chan struct{}),
|
||||
copy: make(chan chan Trie),
|
||||
seen: make(map[string]struct{}),
|
||||
}
|
||||
go sf.loop()
|
||||
return sf
|
||||
@@ -287,12 +295,21 @@ func (sf *subfetcher) loop() {
|
||||
defer close(sf.term)
|
||||
|
||||
// Start by opening the trie and stop processing if it fails
|
||||
trie, err := sf.db.OpenTrie(sf.root)
|
||||
if err != nil {
|
||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
||||
return
|
||||
if sf.owner == (common.Hash{}) {
|
||||
trie, err := sf.db.OpenTrie(sf.root)
|
||||
if err != nil {
|
||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
||||
return
|
||||
}
|
||||
sf.trie = trie
|
||||
} else {
|
||||
trie, err := sf.db.OpenStorageTrie(sf.owner, sf.root)
|
||||
if err != nil {
|
||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
||||
return
|
||||
}
|
||||
sf.trie = trie
|
||||
}
|
||||
sf.trie = trie
|
||||
|
||||
// Trie opened successfully, keep prefetching items
|
||||
for {
|
||||
|
||||
@@ -57,20 +57,20 @@ func TestCopyAndClose(t *testing.T) {
|
||||
db := filledStateDB()
|
||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||
skey := common.HexToHash("aaa")
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
time.Sleep(1 * time.Second)
|
||||
a := prefetcher.trie(db.originalRoot)
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
b := prefetcher.trie(db.originalRoot)
|
||||
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
b := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||
cpy := prefetcher.copy()
|
||||
cpy.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
cpy.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
c := cpy.trie(db.originalRoot)
|
||||
cpy.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
cpy.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
c := cpy.trie(common.Hash{}, db.originalRoot)
|
||||
prefetcher.close()
|
||||
cpy2 := cpy.copy()
|
||||
cpy2.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
d := cpy2.trie(db.originalRoot)
|
||||
cpy2.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
d := cpy2.trie(common.Hash{}, db.originalRoot)
|
||||
cpy.close()
|
||||
cpy2.close()
|
||||
if a.Hash() != b.Hash() || a.Hash() != c.Hash() || a.Hash() != d.Hash() {
|
||||
@@ -82,10 +82,10 @@ func TestUseAfterClose(t *testing.T) {
|
||||
db := filledStateDB()
|
||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||
skey := common.HexToHash("aaa")
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
a := prefetcher.trie(db.originalRoot)
|
||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||
prefetcher.close()
|
||||
b := prefetcher.trie(db.originalRoot)
|
||||
b := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||
if a == nil {
|
||||
t.Fatal("Prefetching before close should not return nil")
|
||||
}
|
||||
@@ -98,13 +98,13 @@ func TestCopyClose(t *testing.T) {
|
||||
db := filledStateDB()
|
||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||
skey := common.HexToHash("aaa")
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
prefetcher.prefetch(common.Hash{}, db.originalRoot, [][]byte{skey.Bytes()})
|
||||
cpy := prefetcher.copy()
|
||||
a := prefetcher.trie(db.originalRoot)
|
||||
b := cpy.trie(db.originalRoot)
|
||||
a := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||
b := cpy.trie(common.Hash{}, db.originalRoot)
|
||||
prefetcher.close()
|
||||
c := prefetcher.trie(db.originalRoot)
|
||||
d := cpy.trie(db.originalRoot)
|
||||
c := prefetcher.trie(common.Hash{}, db.originalRoot)
|
||||
d := cpy.trie(common.Hash{}, db.originalRoot)
|
||||
if a == nil {
|
||||
t.Fatal("Prefetching before close should not return nil")
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestCappedMemoryTrieWriter(t *testing.T) {
|
||||
if i < int(cacheConfig.CommitInterval) {
|
||||
assert.Equal(common.Hash{}, m.LastCommit, "should not have committed block on accept")
|
||||
} else {
|
||||
assert.Equal(block.Root(), m.LastCommit, "should have committed block after commitInterval")
|
||||
assert.Equal(block.Root(), m.LastCommit, "should have committed block after CommitInterval")
|
||||
m.LastCommit = common.Hash{}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,23 +49,7 @@ import (
|
||||
// contain invalid transactions
|
||||
func TestStateProcessorErrors(t *testing.T) {
|
||||
var (
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
FeeConfig: params.DefaultFeeConfig,
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.Hash{},
|
||||
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),
|
||||
NetworkUpgrades: params.NetworkUpgrades{
|
||||
SubnetEVMTimestamp: big.NewInt(0),
|
||||
},
|
||||
}
|
||||
config = params.TestChainConfig
|
||||
signer = types.LatestSigner(config)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
)
|
||||
|
||||
@@ -312,16 +312,22 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
if err := st.preCheck(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg := st.msg
|
||||
sender := vm.AccountRef(msg.From())
|
||||
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
|
||||
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
|
||||
subnetEVM := st.evm.ChainConfig().IsSubnetEVM(st.evm.Context.Time)
|
||||
if st.evm.Config.Debug {
|
||||
st.evm.Config.Tracer.CaptureTxStart(st.initialGas)
|
||||
defer func() {
|
||||
st.evm.Config.Tracer.CaptureTxEnd(st.gas)
|
||||
}()
|
||||
}
|
||||
|
||||
contractCreation := msg.To() == nil
|
||||
var (
|
||||
msg = st.msg
|
||||
sender = vm.AccountRef(msg.From())
|
||||
rules = st.evm.ChainConfig().AvalancheRules(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||
contractCreation = msg.To() == nil
|
||||
)
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -336,7 +342,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
}
|
||||
|
||||
// Set up the initial access list.
|
||||
if rules := st.evm.ChainConfig().AvalancheRules(st.evm.Context.BlockNumber, st.evm.Context.Time); rules.IsSubnetEVM {
|
||||
if rules.IsSubnetEVM {
|
||||
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
}
|
||||
var (
|
||||
@@ -350,7 +356,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
||||
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
||||
}
|
||||
st.refundGas(subnetEVM)
|
||||
st.refundGas(rules.IsSubnetEVM)
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||
|
||||
return &ExecutionResult{
|
||||
|
||||
+3
-3
@@ -68,7 +68,7 @@ func newTxJournal(path string) *txJournal {
|
||||
// the specified pool.
|
||||
func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
|
||||
// Skip the parsing if the journal file doesn't exist at all
|
||||
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
||||
if !common.FileExist(journal.path) {
|
||||
return nil
|
||||
}
|
||||
// Open the journal for loading any past transactions
|
||||
@@ -148,7 +148,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
|
||||
journal.writer = nil
|
||||
}
|
||||
// Generate a new journal with the contents of the current pool
|
||||
replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -168,7 +168,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
|
||||
if err = os.Rename(journal.path+".new", journal.path); err != nil {
|
||||
return err
|
||||
}
|
||||
sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0644)
|
||||
sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-6
@@ -619,7 +619,6 @@ func (pool *TxPool) PendingSize() int {
|
||||
count += len(txs)
|
||||
}
|
||||
return count
|
||||
|
||||
}
|
||||
|
||||
// PendingFrom returns the same set of transactions that would be returned from Pending restricted to only
|
||||
@@ -1631,7 +1630,7 @@ func (pool *TxPool) truncateQueue() {
|
||||
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
|
||||
}
|
||||
}
|
||||
sort.Sort(addresses)
|
||||
sort.Sort(sort.Reverse(addresses))
|
||||
|
||||
// Drop transactions until the total is below the limit or only locals remain
|
||||
for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
|
||||
@@ -1821,10 +1820,6 @@ func (as *accountSet) contains(addr common.Address) bool {
|
||||
return exist
|
||||
}
|
||||
|
||||
func (as *accountSet) empty() bool {
|
||||
return len(as.accounts) == 0
|
||||
}
|
||||
|
||||
// containsTx checks if the sender of a given tx is within the set. If the sender
|
||||
// cannot be derived, this method returns false.
|
||||
func (as *accountSet) containsTx(tx *types.Transaction) bool {
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
@@ -2293,7 +2292,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a temporary file for the journal
|
||||
file, err := ioutil.TempFile("", "")
|
||||
file, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temporary journal: %v", err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// 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
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type AccessTuple -out gen_access_tuple.go
|
||||
//go:generate go run github.com/fjl/gencodec -type AccessTuple -out gen_access_tuple.go
|
||||
|
||||
// AccessList is an EIP-2930 access list.
|
||||
type AccessList []AccessTuple
|
||||
|
||||
+2
-1
@@ -73,7 +73,8 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
|
||||
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
|
||||
}
|
||||
|
||||
//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
||||
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
||||
//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -type Header -out gen_header_rlp.go
|
||||
|
||||
// Header represents a block header in the Ethereum blockchain.
|
||||
type Header struct {
|
||||
|
||||
@@ -66,13 +66,13 @@ func TestBloom(t *testing.T) {
|
||||
|
||||
// TestBloomExtensively does some more thorough tests
|
||||
func TestBloomExtensively(t *testing.T) {
|
||||
var exp = common.HexToHash("c8d3ca65cdb4874300a9e39475508f23ed6da09fdbc487f89a2dcf50b09eb263")
|
||||
exp := common.HexToHash("c8d3ca65cdb4874300a9e39475508f23ed6da09fdbc487f89a2dcf50b09eb263")
|
||||
var b Bloom
|
||||
// Add 100 "random" things
|
||||
for i := 0; i < 100; i++ {
|
||||
data := fmt.Sprintf("xxxxxxxxxx data %d yyyyyyyyyyyyyy", i)
|
||||
b.Add([]byte(data))
|
||||
//b.Add(new(big.Int).SetBytes([]byte(data)))
|
||||
// b.Add(new(big.Int).SetBytes([]byte(data)))
|
||||
}
|
||||
got := crypto.Keccak256Hash(b.Bytes())
|
||||
if got != exp {
|
||||
@@ -102,12 +102,11 @@ func BenchmarkBloom9Lookup(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkCreateBloom(b *testing.B) {
|
||||
|
||||
var txs = Transactions{
|
||||
txs := Transactions{
|
||||
NewContractCreation(1, big.NewInt(1), 1, big.NewInt(1), nil),
|
||||
NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil),
|
||||
}
|
||||
var rSmall = Receipts{
|
||||
rSmall := Receipts{
|
||||
&Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
@@ -132,7 +131,7 @@ func BenchmarkCreateBloom(b *testing.B) {
|
||||
},
|
||||
}
|
||||
|
||||
var rLarge = make(Receipts, 200)
|
||||
rLarge := make(Receipts, 200)
|
||||
// Fill it with 200 receipts x 2 logs
|
||||
for i := 0; i < 200; i += 2 {
|
||||
copy(rLarge[i:], rSmall)
|
||||
@@ -144,7 +143,7 @@ func BenchmarkCreateBloom(b *testing.B) {
|
||||
bl = CreateBloom(rSmall)
|
||||
}
|
||||
b.StopTimer()
|
||||
var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
||||
exp := common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
||||
got := crypto.Keccak256Hash(bl.Bytes())
|
||||
if got != exp {
|
||||
b.Errorf("Got %x, exp %x", got, exp)
|
||||
@@ -157,7 +156,7 @@ func BenchmarkCreateBloom(b *testing.B) {
|
||||
bl = CreateBloom(rLarge)
|
||||
}
|
||||
b.StopTimer()
|
||||
var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
||||
exp := common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
|
||||
got := crypto.Keccak256Hash(bl.Bytes())
|
||||
if got != exp {
|
||||
b.Errorf("Got %x, exp %x", got, exp)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Code generated by rlpgen. DO NOT EDIT.
|
||||
|
||||
//go:build !norlpgen
|
||||
// +build !norlpgen
|
||||
|
||||
package types
|
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp"
|
||||
import "io"
|
||||
|
||||
func (obj *StateAccount) EncodeRLP(_w io.Writer) error {
|
||||
w := rlp.NewEncoderBuffer(_w)
|
||||
_tmp0 := w.List()
|
||||
w.WriteUint64(obj.Nonce)
|
||||
if obj.Balance == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
if obj.Balance.Sign() == -1 {
|
||||
return rlp.ErrNegativeBigInt
|
||||
}
|
||||
w.WriteBigInt(obj.Balance)
|
||||
}
|
||||
w.WriteBytes(obj.Root[:])
|
||||
w.WriteBytes(obj.CodeHash)
|
||||
w.ListEnd(_tmp0)
|
||||
return w.Flush()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Code generated by rlpgen. DO NOT EDIT.
|
||||
|
||||
//go:build !norlpgen
|
||||
// +build !norlpgen
|
||||
|
||||
package types
|
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp"
|
||||
import "io"
|
||||
|
||||
func (obj *Header) EncodeRLP(_w io.Writer) error {
|
||||
w := rlp.NewEncoderBuffer(_w)
|
||||
_tmp0 := w.List()
|
||||
w.WriteBytes(obj.ParentHash[:])
|
||||
w.WriteBytes(obj.UncleHash[:])
|
||||
w.WriteBytes(obj.Coinbase[:])
|
||||
w.WriteBytes(obj.Root[:])
|
||||
w.WriteBytes(obj.TxHash[:])
|
||||
w.WriteBytes(obj.ReceiptHash[:])
|
||||
w.WriteBytes(obj.Bloom[:])
|
||||
if obj.Difficulty == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
if obj.Difficulty.Sign() == -1 {
|
||||
return rlp.ErrNegativeBigInt
|
||||
}
|
||||
w.WriteBigInt(obj.Difficulty)
|
||||
}
|
||||
if obj.Number == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
if obj.Number.Sign() == -1 {
|
||||
return rlp.ErrNegativeBigInt
|
||||
}
|
||||
w.WriteBigInt(obj.Number)
|
||||
}
|
||||
w.WriteUint64(obj.GasLimit)
|
||||
w.WriteUint64(obj.GasUsed)
|
||||
w.WriteUint64(obj.Time)
|
||||
w.WriteBytes(obj.Extra)
|
||||
w.WriteBytes(obj.MixDigest[:])
|
||||
w.WriteBytes(obj.Nonce[:])
|
||||
_tmp1 := obj.BaseFee != nil
|
||||
_tmp2 := obj.BlockGasCost != nil
|
||||
if _tmp1 || _tmp2 {
|
||||
if obj.BaseFee == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
if obj.BaseFee.Sign() == -1 {
|
||||
return rlp.ErrNegativeBigInt
|
||||
}
|
||||
w.WriteBigInt(obj.BaseFee)
|
||||
}
|
||||
}
|
||||
if _tmp2 {
|
||||
if obj.BlockGasCost == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
if obj.BlockGasCost.Sign() == -1 {
|
||||
return rlp.ErrNegativeBigInt
|
||||
}
|
||||
w.WriteBigInt(obj.BlockGasCost)
|
||||
}
|
||||
}
|
||||
w.ListEnd(_tmp0)
|
||||
return w.Flush()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Code generated by rlpgen. DO NOT EDIT.
|
||||
|
||||
//go:build !norlpgen
|
||||
// +build !norlpgen
|
||||
|
||||
package types
|
||||
|
||||
import "github.com/ethereum/go-ethereum/rlp"
|
||||
import "io"
|
||||
|
||||
func (obj *rlpLog) EncodeRLP(_w io.Writer) error {
|
||||
w := rlp.NewEncoderBuffer(_w)
|
||||
_tmp0 := w.List()
|
||||
w.WriteBytes(obj.Address[:])
|
||||
_tmp1 := w.List()
|
||||
for _, _tmp2 := range obj.Topics {
|
||||
w.WriteBytes(_tmp2[:])
|
||||
}
|
||||
w.ListEnd(_tmp1)
|
||||
w.WriteBytes(obj.Data)
|
||||
w.ListEnd(_tmp0)
|
||||
return w.Flush()
|
||||
}
|
||||
@@ -41,7 +41,7 @@ var hasherPool = sync.Pool{
|
||||
New: func() interface{} { return sha3.NewLegacyKeccak256() },
|
||||
}
|
||||
|
||||
// deriveBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
|
||||
var encodeBufferPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/core/rawdb"
|
||||
"github.com/ava-labs/subnet-evm/core/types"
|
||||
"github.com/ava-labs/subnet-evm/trie"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -48,7 +49,7 @@ func TestDeriveSha(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for len(txs) < 1000 {
|
||||
exp := types.DeriveSha(txs, new(trie.Trie))
|
||||
exp := types.DeriveSha(txs, trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase())))
|
||||
got := types.DeriveSha(txs, trie.NewStackTrie(nil))
|
||||
if !bytes.Equal(got[:], exp[:]) {
|
||||
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp)
|
||||
@@ -95,7 +96,7 @@ func BenchmarkDeriveSha200(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
exp = types.DeriveSha(txs, new(trie.Trie))
|
||||
exp = types.DeriveSha(txs, trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase())))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -116,7 +117,7 @@ func TestFuzzDeriveSha(t *testing.T) {
|
||||
rndSeed := mrand.Int()
|
||||
for i := 0; i < 10; i++ {
|
||||
seed := rndSeed + i
|
||||
exp := types.DeriveSha(newDummy(i), new(trie.Trie))
|
||||
exp := types.DeriveSha(newDummy(i), trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase())))
|
||||
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
|
||||
if !bytes.Equal(got[:], exp[:]) {
|
||||
printList(newDummy(seed))
|
||||
@@ -144,7 +145,7 @@ func TestDerivableList(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, tc := range tcs[1:] {
|
||||
exp := types.DeriveSha(flatList(tc), new(trie.Trie))
|
||||
exp := types.DeriveSha(flatList(tc), trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase())))
|
||||
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
|
||||
if !bytes.Equal(got[:], exp[:]) {
|
||||
t.Fatalf("case %d: got %x exp %x", i, got, exp)
|
||||
@@ -206,7 +207,7 @@ func printList(l types.DerivableList) {
|
||||
for i := 0; i < l.Len(); i++ {
|
||||
var buf bytes.Buffer
|
||||
l.EncodeIndex(i, &buf)
|
||||
fmt.Printf("\"0x%x\",\n", buf.Bytes())
|
||||
fmt.Printf("\"%#x\",\n", buf.Bytes())
|
||||
}
|
||||
fmt.Printf("},\n")
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
// Much love to the original authors for their work.
|
||||
// **********
|
||||
// Copyright 2020 The go-ethereum Authors
|
||||
// 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
|
||||
|
||||
+10
-13
@@ -34,7 +34,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
||||
//go:generate go run github.com/fjl/gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
||||
|
||||
// Log represents a contract log event. These events are generated by the LOG opcode and
|
||||
// stored/indexed by the node.
|
||||
@@ -72,15 +72,14 @@ type logMarshaling struct {
|
||||
Index hexutil.Uint
|
||||
}
|
||||
|
||||
//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -type rlpLog -out gen_log_rlp.go
|
||||
|
||||
type rlpLog struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// rlpStorageLog is the storage encoding of a log.
|
||||
type rlpStorageLog rlpLog
|
||||
|
||||
// legacyRlpStorageLog is the previous storage encoding of a log including some redundant fields.
|
||||
type legacyRlpStorageLog struct {
|
||||
Address common.Address
|
||||
@@ -95,7 +94,8 @@ type legacyRlpStorageLog struct {
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *Log) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
|
||||
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
|
||||
return rlp.Encode(w, &rl)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
@@ -108,17 +108,14 @@ func (l *Log) DecodeRLP(s *rlp.Stream) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
|
||||
// a log including non-consensus fields.
|
||||
// LogForStorage is a wrapper around a Log that handles
|
||||
// backward compatibility with prior storage formats.
|
||||
type LogForStorage Log
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *LogForStorage) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpStorageLog{
|
||||
Address: l.Address,
|
||||
Topics: l.Topics,
|
||||
Data: l.Data,
|
||||
})
|
||||
rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
|
||||
return rlp.Encode(w, &rl)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
@@ -129,7 +126,7 @@ func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var dec rlpStorageLog
|
||||
var dec rlpLog
|
||||
err = rlp.DecodeBytes(blob, &dec)
|
||||
if err == nil {
|
||||
*l = LogForStorage{
|
||||
|
||||
+21
-31
@@ -41,15 +41,14 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
|
||||
//go:generate go run github.com/fjl/gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
|
||||
|
||||
var (
|
||||
receiptStatusFailedRLP = []byte{}
|
||||
receiptStatusSuccessfulRLP = []byte{0x01}
|
||||
)
|
||||
|
||||
// This error is returned when a typed receipt is decoded, but the string is empty.
|
||||
var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
|
||||
var errShortTypedReceipt = errors.New("typed receipt too short")
|
||||
|
||||
const (
|
||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||
@@ -192,26 +191,13 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(dec)
|
||||
case kind == rlp.String:
|
||||
default:
|
||||
// It's an EIP-2718 typed tx receipt.
|
||||
b, err := s.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errEmptyTypedReceipt
|
||||
}
|
||||
r.Type = b[0]
|
||||
if r.Type == AccessListTxType || r.Type == DynamicFeeTxType {
|
||||
var dec receiptRLP
|
||||
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.setFromRLP(dec)
|
||||
}
|
||||
return ErrTxTypeNotSupported
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
return r.decodeTyped(b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,8 +220,8 @@ func (r *Receipt) UnmarshalBinary(b []byte) error {
|
||||
|
||||
// decodeTyped decodes a typed receipt from the canonical format.
|
||||
func (r *Receipt) decodeTyped(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
return errEmptyTypedReceipt
|
||||
if len(b) <= 1 {
|
||||
return errShortTypedReceipt
|
||||
}
|
||||
switch b[0] {
|
||||
case DynamicFeeTxType, AccessListTxType:
|
||||
@@ -291,22 +277,26 @@ func (r *Receipt) Size() common.StorageSize {
|
||||
return size
|
||||
}
|
||||
|
||||
// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the
|
||||
// entire content of a receipt, as opposed to only the consensus fields originally.
|
||||
// ReceiptForStorage is a wrapper around a Receipt with RLP serialization
|
||||
// that omits the Bloom field and deserialization that re-computes it.
|
||||
type ReceiptForStorage Receipt
|
||||
|
||||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||
// into an RLP stream.
|
||||
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
enc := &storedReceiptRLP{
|
||||
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
||||
CumulativeGasUsed: r.CumulativeGasUsed,
|
||||
Logs: make([]*LogForStorage, len(r.Logs)),
|
||||
func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
||||
w := rlp.NewEncoderBuffer(_w)
|
||||
outerList := w.List()
|
||||
w.WriteBytes((*Receipt)(r).statusEncoding())
|
||||
w.WriteUint64(r.CumulativeGasUsed)
|
||||
logList := w.List()
|
||||
for _, log := range r.Logs {
|
||||
if err := rlp.Encode(w, log); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i, log := range r.Logs {
|
||||
enc.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.Encode(w, enc)
|
||||
w.ListEnd(logList)
|
||||
w.ListEnd(outerList)
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
|
||||
|
||||
@@ -96,7 +96,7 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var r Receipt
|
||||
err := rlp.DecodeBytes(input, &r)
|
||||
if err != errEmptyTypedReceipt {
|
||||
if err != errShortTypedReceipt {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -type StateAccount -out gen_account_rlp.go
|
||||
|
||||
// StateAccount is the Ethereum consensus representation of accounts.
|
||||
// These objects are stored in the main account trie.
|
||||
type StateAccount struct {
|
||||
|
||||
@@ -47,7 +47,7 @@ var (
|
||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
||||
errShortTypedTx = errors.New("typed transaction too short")
|
||||
)
|
||||
|
||||
// Transaction types.
|
||||
@@ -144,7 +144,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
||||
}
|
||||
return err
|
||||
case kind == rlp.String:
|
||||
default:
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
var b []byte
|
||||
if b, err = s.Bytes(); err != nil {
|
||||
@@ -155,8 +155,6 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
tx.setDecoded(inner, len(b))
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,8 +182,8 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
||||
|
||||
// decodeTyped decodes a typed transaction from the canonical format.
|
||||
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, errEmptyTypedTx
|
||||
if len(b) <= 1 {
|
||||
return nil, errShortTypedTx
|
||||
}
|
||||
switch b[0] {
|
||||
case AccessListTxType:
|
||||
@@ -454,6 +452,24 @@ func TxDifference(a, b Transactions) Transactions {
|
||||
return keep
|
||||
}
|
||||
|
||||
// HashDifference returns a new set which is the difference between a and b.
|
||||
func HashDifference(a, b []common.Hash) []common.Hash {
|
||||
keep := make([]common.Hash, 0, len(a))
|
||||
|
||||
remove := make(map[common.Hash]struct{})
|
||||
for _, hash := range b {
|
||||
remove[hash] = struct{}{}
|
||||
}
|
||||
|
||||
for _, hash := range a {
|
||||
if _, ok := remove[hash]; !ok {
|
||||
keep = append(keep, hash)
|
||||
}
|
||||
}
|
||||
|
||||
return keep
|
||||
}
|
||||
|
||||
// TxByNonce implements the sort interface to allow sorting a list of transactions
|
||||
// by their nonces. This is usually only useful for sorting transactions from a
|
||||
// single account, otherwise a nonce comparison doesn't make much sense.
|
||||
|
||||
@@ -121,7 +121,6 @@ func TestEIP155SigningVitalik(t *testing.T) {
|
||||
if from != addr {
|
||||
t.Errorf("%d: expected %x got %x", i, addr, from)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestDecodeEmptyTypedTx(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var tx Transaction
|
||||
err := rlp.DecodeBytes(input, &tx)
|
||||
if err != errEmptyTypedTx {
|
||||
if err != errShortTypedTx {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,6 @@ func TestEIP2718TransactionSigHash(t *testing.T) {
|
||||
|
||||
// This test checks signature operations on access list transactions.
|
||||
func TestEIP2930Signer(t *testing.T) {
|
||||
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
keyAddr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
@@ -487,14 +486,17 @@ func TestTransactionCoding(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(parsedTx, tx)
|
||||
|
||||
if err := assertEqual(parsedTx, tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// JSON
|
||||
parsedTx, err = encodeDecodeJSON(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqual(parsedTx, tx)
|
||||
if err := assertEqual(parsedTx, tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,7 +505,7 @@ func encodeDecodeJSON(tx *Transaction) (*Transaction, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json encoding failed: %v", err)
|
||||
}
|
||||
var parsedTx = &Transaction{}
|
||||
parsedTx := &Transaction{}
|
||||
if err := json.Unmarshal(data, &parsedTx); err != nil {
|
||||
return nil, fmt.Errorf("json decoding failed: %v", err)
|
||||
}
|
||||
@@ -515,7 +517,7 @@ func encodeDecodeBinary(tx *Transaction) (*Transaction, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rlp encoding failed: %v", err)
|
||||
}
|
||||
var parsedTx = &Transaction{}
|
||||
parsedTx := &Transaction{}
|
||||
if err := parsedTx.UnmarshalBinary(data); err != nil {
|
||||
return nil, fmt.Errorf("rlp decoding failed: %v", err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ func codeBitmapInternal(code, bits bitvec) bitvec {
|
||||
for pc := uint64(0); pc < uint64(len(code)); {
|
||||
op := OpCode(code[pc])
|
||||
pc++
|
||||
if op < PUSH1 || op > PUSH32 {
|
||||
if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false).
|
||||
continue
|
||||
}
|
||||
numbits := op - PUSH1 + 1
|
||||
|
||||
@@ -44,7 +44,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto/bls12381"
|
||||
"github.com/ethereum/go-ethereum/crypto/bn256"
|
||||
|
||||
//lint:ignore SA1019 Needed for precompile
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
@@ -635,7 +634,7 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
|
||||
// Parse the input into the Blake2b call parameters
|
||||
var (
|
||||
rounds = binary.BigEndian.Uint32(input[0:4])
|
||||
final = (input[212] == blake2FFinalBlockBytes)
|
||||
final = input[212] == blake2FFinalBlockBytes
|
||||
|
||||
h [8]uint64
|
||||
m [16]uint64
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -189,7 +189,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
|
||||
// Keep it as uint64, multiply 100 to get two digit float later
|
||||
mgasps := (100 * 1000 * gasUsed) / elapsed
|
||||
bench.ReportMetric(float64(mgasps)/100, "mgas/s")
|
||||
//Check if it is correct
|
||||
// Check if it is correct
|
||||
if err != nil {
|
||||
bench.Error(err)
|
||||
return
|
||||
@@ -344,7 +344,7 @@ func TestPrecompiledBLS12381MapG1Fail(t *testing.T) { testJsonFail("blsMapG
|
||||
func TestPrecompiledBLS12381MapG2Fail(t *testing.T) { testJsonFail("blsMapG2", "12", t) }
|
||||
|
||||
func loadJson(name string) ([]precompiledTest, error) {
|
||||
data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name))
|
||||
data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -354,7 +354,7 @@ func loadJson(name string) ([]precompiledTest, error) {
|
||||
}
|
||||
|
||||
func loadJsonFail(name string) ([]precompiledFailureTest, error) {
|
||||
data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/fail-%v.json", name))
|
||||
data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/fail-%v.json", name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
)
|
||||
|
||||
var activators = map[int]func(*JumpTable){
|
||||
3855: enable3855,
|
||||
3198: enable3198,
|
||||
2929: enable2929,
|
||||
2200: enable2200,
|
||||
@@ -175,3 +176,20 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||
scope.Stack.push(baseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// enable3855 applies EIP-3855 (PUSH0 opcode)
|
||||
func enable3855(jt *JumpTable) {
|
||||
// New opcode
|
||||
jt[PUSH0] = &operation{
|
||||
execute: opPush0,
|
||||
constantGas: GasQuickStep,
|
||||
minStack: minStack(0, 1),
|
||||
maxStack: maxStack(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// opPush0 implements the PUSH0 opcode
|
||||
func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// (c) 2022, Ava Labs, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsProhibited(t *testing.T) {
|
||||
// reserved addresses (coreth)
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x0100000000000000000000000000000000000000")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x0100000000000000000000000000000000000010")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x01000000000000000000000000000000000000f0")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x01000000000000000000000000000000000000ff")))
|
||||
// reserved addresses (subnet-evm)
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x0200000000000000000000000000000000000000")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x0200000000000000000000000000000000000010")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x02000000000000000000000000000000000000f0")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x02000000000000000000000000000000000000ff")))
|
||||
// reserved addresses (custom precompiles)
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x0300000000000000000000000000000000000000")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x0300000000000000000000000000000000000010")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x03000000000000000000000000000000000000f0")))
|
||||
assert.True(t, IsProhibited(common.HexToAddress("0x03000000000000000000000000000000000000ff")))
|
||||
|
||||
// allowed for use
|
||||
assert.False(t, IsProhibited(common.HexToAddress("0x00000000000000000000000000000000000000ff")))
|
||||
assert.False(t, IsProhibited(common.HexToAddress("0x0100000000000000000000000000000000000100")))
|
||||
assert.False(t, IsProhibited(common.HexToAddress("0x0200000000000000000000000000000000000100")))
|
||||
assert.False(t, IsProhibited(common.HexToAddress("0x0300000000000000000000000000000000000100")))
|
||||
}
|
||||
@@ -30,8 +30,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/params"
|
||||
@@ -240,38 +240,38 @@ func TestAddMod(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// getResult is a convenience function to generate the expected values
|
||||
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
interpreter = env.interpreter
|
||||
)
|
||||
result := make([]TwoOperandTestcase, len(args))
|
||||
for i, param := range args {
|
||||
x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
|
||||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
|
||||
actual := stack.pop()
|
||||
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// utility function to fill the json-file with testcases
|
||||
// Enable this test to generate the 'testcases_xx.json' files
|
||||
func TestWriteExpectedValues(t *testing.T) {
|
||||
t.Skip("Enable this test to create json test cases.")
|
||||
|
||||
// getResult is a convenience function to generate the expected values
|
||||
getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
interpreter = env.interpreter
|
||||
)
|
||||
result := make([]TwoOperandTestcase, len(args))
|
||||
for i, param := range args {
|
||||
x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
|
||||
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
|
||||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
|
||||
actual := stack.pop()
|
||||
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
for name, method := range twoOpMethods {
|
||||
data, err := json.Marshal(getResult(commonParams, method))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = ioutil.WriteFile(fmt.Sprintf("testdata/testcases_%v.json", name), data, 0644)
|
||||
_ = os.WriteFile(fmt.Sprintf("testdata/testcases_%v.json", name), data, 0o644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func TestWriteExpectedValues(t *testing.T) {
|
||||
// TestJsonTestcases runs through all the testcases defined as json-files
|
||||
func TestJsonTestcases(t *testing.T) {
|
||||
for name := range twoOpMethods {
|
||||
data, err := ioutil.ReadFile(fmt.Sprintf("testdata/testcases_%v.json", name))
|
||||
data, err := os.ReadFile(fmt.Sprintf("testdata/testcases_%v.json", name))
|
||||
if err != nil {
|
||||
t.Fatal("Failed to read file", err)
|
||||
}
|
||||
@@ -295,26 +295,33 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
||||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
scope = &ScopeContext{nil, stack, nil}
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
// convert args
|
||||
byteArgs := make([][]byte, len(args))
|
||||
intArgs := make([]*uint256.Int, len(args))
|
||||
for i, arg := range args {
|
||||
byteArgs[i] = common.Hex2Bytes(arg)
|
||||
intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
||||
}
|
||||
pc := uint64(0)
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
for _, arg := range byteArgs {
|
||||
a := new(uint256.Int)
|
||||
a.SetBytes(arg)
|
||||
stack.push(a)
|
||||
for _, arg := range intArgs {
|
||||
stack.push(arg)
|
||||
}
|
||||
op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
op(&pc, evmInterpreter, scope)
|
||||
stack.pop()
|
||||
}
|
||||
bench.StopTimer()
|
||||
|
||||
for i, arg := range args {
|
||||
want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
||||
if have := intArgs[i]; !want.Eq(have) {
|
||||
bench.Fatalf("input #%d mutated, have %x want %x", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOpAdd64(b *testing.B) {
|
||||
@@ -650,7 +657,6 @@ func TestCreate2Addreses(t *testing.T) {
|
||||
expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0",
|
||||
},
|
||||
} {
|
||||
|
||||
origin := common.BytesToAddress(common.FromHex(tt.origin))
|
||||
salt := common.BytesToHash(common.FromHex(tt.salt))
|
||||
code := common.FromHex(tt.code)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user