ethapi: introduce tests for transaction options
This commit is contained in:
parent
26c69a15ba
commit
f8c5f55652
@ -680,7 +680,14 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||
|
||||
// SendTransaction updates the pending block to include the given transaction.
|
||||
func (b *SimulatedBackend) SendTransactionConditional(ctx context.Context, tx *types.Transaction, opts ethapi.TransactionOpts) error {
|
||||
return nil
|
||||
state, err := b.blockchain.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := opts.Check(b.pendingBlock.NumberU64(), b.pendingBlock.Time(), state); err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendTransaction(ctx, tx)
|
||||
}
|
||||
|
||||
// FilterLogs executes a log filter operation, blocking during execution and
|
||||
|
@ -2144,11 +2144,12 @@ func (s *PublicTransactionPoolAPI) SendRawTransactionConditional(ctx context.Con
|
||||
if err := tx.UnmarshalBinary(input); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(s.b.CurrentBlock().Number().Int64()))
|
||||
header := s.b.CurrentHeader()
|
||||
state, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(header.Number.Int64()))
|
||||
if state == nil || err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
if err := opts.Check(s.b.CurrentBlock().NumberU64(), s.b.CurrentBlock().Time(), state); err != nil {
|
||||
if err := opts.Check(header.Number.Uint64(), header.Time, state); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
return SubmitTransaction(ctx, s.b, tx)
|
||||
|
58
internal/ethapi/gen_tx_opts_json.go
Normal file
58
internal/ethapi/gen_tx_opts_json.go
Normal file
@ -0,0 +1,58 @@
|
||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (t TransactionOpts) MarshalJSON() ([]byte, error) {
|
||||
type TransactionOpts struct {
|
||||
KnownAccounts KnownAccounts `json:"knownAccounts"`
|
||||
BlockNumberMin *hexutil.Uint64 `json:"blockNumberMin,omitempty"`
|
||||
BlockNumberMax *hexutil.Uint64 `json:"blockNumberMax,omitempty"`
|
||||
TimestampMin *hexutil.Uint64 `json:"timestampMin,omitempty"`
|
||||
TimestampMax *hexutil.Uint64 `json:"timestampMax,omitempty"`
|
||||
}
|
||||
var enc TransactionOpts
|
||||
enc.KnownAccounts = t.KnownAccounts
|
||||
enc.BlockNumberMin = t.BlockNumberMin
|
||||
enc.BlockNumberMax = t.BlockNumberMax
|
||||
enc.TimestampMin = t.TimestampMin
|
||||
enc.TimestampMax = t.TimestampMax
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (t *TransactionOpts) UnmarshalJSON(input []byte) error {
|
||||
type TransactionOpts struct {
|
||||
KnownAccounts *KnownAccounts `json:"knownAccounts"`
|
||||
BlockNumberMin *hexutil.Uint64 `json:"blockNumberMin,omitempty"`
|
||||
BlockNumberMax *hexutil.Uint64 `json:"blockNumberMax,omitempty"`
|
||||
TimestampMin *hexutil.Uint64 `json:"timestampMin,omitempty"`
|
||||
TimestampMax *hexutil.Uint64 `json:"timestampMax,omitempty"`
|
||||
}
|
||||
var dec TransactionOpts
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.KnownAccounts != nil {
|
||||
t.KnownAccounts = *dec.KnownAccounts
|
||||
}
|
||||
if dec.BlockNumberMin != nil {
|
||||
t.BlockNumberMin = dec.BlockNumberMin
|
||||
}
|
||||
if dec.BlockNumberMax != nil {
|
||||
t.BlockNumberMax = dec.BlockNumberMax
|
||||
}
|
||||
if dec.TimestampMin != nil {
|
||||
t.TimestampMin = dec.TimestampMin
|
||||
}
|
||||
if dec.TimestampMax != nil {
|
||||
t.TimestampMax = dec.TimestampMax
|
||||
}
|
||||
return nil
|
||||
}
|
@ -11,28 +11,34 @@ import (
|
||||
)
|
||||
|
||||
type AccountStorage struct {
|
||||
RootHash *common.Hash
|
||||
SlotValue map[common.Hash]common.Hash
|
||||
StorageRoot *common.Hash
|
||||
StorageSlots map[common.Hash]common.Hash
|
||||
}
|
||||
|
||||
func (r *AccountStorage) UnmarshalJSON(data []byte) error {
|
||||
func (a *AccountStorage) UnmarshalJSON(data []byte) error {
|
||||
var hash common.Hash
|
||||
if err := json.Unmarshal(data, &hash); err == nil {
|
||||
r.RootHash = &hash
|
||||
a.StorageRoot = &hash
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, &r.SlotValue)
|
||||
return json.Unmarshal(data, &a.StorageSlots)
|
||||
}
|
||||
|
||||
func (r AccountStorage) MarshalJSON() ([]byte, error) {
|
||||
if r.RootHash != nil {
|
||||
return json.Marshal(*r.RootHash)
|
||||
func (a AccountStorage) MarshalJSON() ([]byte, error) {
|
||||
if a.StorageRoot != nil {
|
||||
return json.Marshal(*a.StorageRoot)
|
||||
}
|
||||
return json.Marshal(r.SlotValue)
|
||||
return json.Marshal(a.StorageSlots)
|
||||
}
|
||||
|
||||
type KnownAccounts map[common.Address]AccountStorage
|
||||
|
||||
// It is known that marshaling is broken
|
||||
// https://github.com/golang/go/issues/55890
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type TransactionOpts -out gen_tx_opts_json.go
|
||||
type TransactionOpts struct {
|
||||
KnownAccounts map[common.Address]AccountStorage `json:"knownAccounts"`
|
||||
KnownAccounts KnownAccounts `json:"knownAccounts"`
|
||||
BlockNumberMin *hexutil.Uint64 `json:"blockNumberMin,omitempty"`
|
||||
BlockNumberMax *hexutil.Uint64 `json:"blockNumberMax,omitempty"`
|
||||
TimestampMin *hexutil.Uint64 `json:"timestampMin,omitempty"`
|
||||
@ -54,30 +60,30 @@ func (o *TransactionOpts) Check(blockNumber uint64, timeStamp uint64, statedb *s
|
||||
}
|
||||
counter := 0
|
||||
for _, account := range o.KnownAccounts {
|
||||
if account.RootHash != nil {
|
||||
if account.StorageRoot != nil {
|
||||
counter += 1
|
||||
} else if account.SlotValue != nil {
|
||||
counter += len(account.SlotValue)
|
||||
} else if account.StorageSlots != nil {
|
||||
counter += len(account.StorageSlots)
|
||||
}
|
||||
}
|
||||
if counter > 1000 {
|
||||
return errors.New("knownAccounts too large")
|
||||
}
|
||||
return o.CheckOnlyStorage(statedb)
|
||||
return o.CheckStorage(statedb)
|
||||
}
|
||||
|
||||
func (o *TransactionOpts) CheckOnlyStorage(statedb *state.StateDB) error {
|
||||
func (o *TransactionOpts) CheckStorage(statedb *state.StateDB) error {
|
||||
for address, accountStorage := range o.KnownAccounts {
|
||||
if accountStorage.RootHash != nil {
|
||||
if accountStorage.StorageRoot != nil {
|
||||
trie := statedb.StorageTrie(address)
|
||||
if trie == nil {
|
||||
return errors.New("storage trie not found for address key in knownAccounts option")
|
||||
}
|
||||
if trie.Hash() != *accountStorage.RootHash {
|
||||
if trie.Hash() != *accountStorage.StorageRoot {
|
||||
return errors.New("storage root hash condition not met")
|
||||
}
|
||||
} else if len(accountStorage.SlotValue) > 0 {
|
||||
for slot, value := range accountStorage.SlotValue {
|
||||
} else if len(accountStorage.StorageSlots) > 0 {
|
||||
for slot, value := range accountStorage.StorageSlots {
|
||||
stored := statedb.GetState(address, slot)
|
||||
if !bytes.Equal(stored.Bytes(), value.Bytes()) {
|
||||
return errors.New("storage slot value condition not met")
|
||||
|
122
internal/ethapi/transaction_options_test.go
Normal file
122
internal/ethapi/transaction_options_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
package ethapi_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
)
|
||||
|
||||
func ptr(hash common.Hash) *common.Hash {
|
||||
return &hash
|
||||
}
|
||||
|
||||
func u64Ptr(v hexutil.Uint64) *hexutil.Uint64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func TestTransactionOptsJSONUnMarshalTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
mustFail bool
|
||||
expected ethapi.TransactionOpts
|
||||
}{
|
||||
{
|
||||
"StateRoot",
|
||||
`{"knownAccounts":{"0x6b3A8798E5Fb9fC5603F3aB5eA2e8136694e55d0":"0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"}}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
KnownAccounts: map[common.Address]ethapi.AccountStorage{
|
||||
common.HexToAddress("0x6b3A8798E5Fb9fC5603F3aB5eA2e8136694e55d0"): ethapi.AccountStorage{
|
||||
StorageRoot: ptr(common.HexToHash("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"StorageSlots",
|
||||
`{"knownAccounts":{"0x6b3A8798E5Fb9fC5603F3aB5eA2e8136694e55d0":{"0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8":"0x0000000000000000000000000000000000000000000000000000000000000000"}}}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
KnownAccounts: map[common.Address]ethapi.AccountStorage{
|
||||
common.HexToAddress("0x6b3A8798E5Fb9fC5603F3aB5eA2e8136694e55d0"): ethapi.AccountStorage{
|
||||
StorageRoot: nil,
|
||||
StorageSlots: map[common.Hash]common.Hash{
|
||||
common.HexToHash("0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8"): common.HexToHash("0x"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"EmptyObject",
|
||||
`{"knownAccounts":{}}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
KnownAccounts: make(map[common.Address]ethapi.AccountStorage),
|
||||
},
|
||||
},
|
||||
{
|
||||
"EmptyStrings",
|
||||
`{"knownAccounts":{"":""}}`,
|
||||
true,
|
||||
ethapi.TransactionOpts{
|
||||
KnownAccounts: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
"BlockNumberMin",
|
||||
`{"blockNumberMin":"0x1"}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
BlockNumberMin: u64Ptr(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
"BlockNumberMax",
|
||||
`{"blockNumberMin":"0x1", "blockNumberMax":"0x2"}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
BlockNumberMin: u64Ptr(1),
|
||||
BlockNumberMax: u64Ptr(2),
|
||||
},
|
||||
},
|
||||
{
|
||||
"TimestampMin",
|
||||
`{"timestampMin":"0xffff"}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
TimestampMin: u64Ptr(0xffff),
|
||||
},
|
||||
},
|
||||
{
|
||||
"TimestampMax",
|
||||
`{"timestampMax":"0xffffff"}`,
|
||||
false,
|
||||
ethapi.TransactionOpts{
|
||||
TimestampMax: u64Ptr(0xffffff),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var opts ethapi.TransactionOpts
|
||||
err := json.Unmarshal([]byte(test.input), &opts)
|
||||
if test.mustFail && err == nil {
|
||||
t.Errorf("Test %s should fail", test.name)
|
||||
}
|
||||
if !test.mustFail && err != nil {
|
||||
t.Errorf("Test %s should pass but got err: %v", test.name, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(opts, test.expected) {
|
||||
t.Errorf("Test %s got unexpected value, want %#v, got %#v", test.name, test.expected, opts)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user