bsc/internal/ethapi/transaction_options.go
2023-04-14 15:23:07 +02:00

95 lines
2.8 KiB
Go

package ethapi
import (
"bytes"
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
)
type AccountStorage struct {
StorageRoot *common.Hash
StorageSlots map[common.Hash]common.Hash
}
func (a *AccountStorage) UnmarshalJSON(data []byte) error {
var hash common.Hash
if err := json.Unmarshal(data, &hash); err == nil {
a.StorageRoot = &hash
return nil
}
return json.Unmarshal(data, &a.StorageSlots)
}
func (a AccountStorage) MarshalJSON() ([]byte, error) {
if a.StorageRoot != nil {
return json.Marshal(*a.StorageRoot)
}
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 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"`
}
const MaxNumberOfEntries = 1000
func (o *TransactionOpts) Check(blockNumber uint64, timeStamp uint64, statedb *state.StateDB) error {
if o.BlockNumberMin != nil && blockNumber < uint64(*o.BlockNumberMin) {
return errors.New("BlockNumberMin condition not met")
}
if o.BlockNumberMax != nil && blockNumber > uint64(*o.BlockNumberMax) {
return errors.New("BlockNumberMax condition not met")
}
if o.TimestampMin != nil && timeStamp < uint64(*o.TimestampMin) {
return errors.New("TimestampMin condition not met")
}
if o.TimestampMax != nil && timeStamp > uint64(*o.TimestampMax) {
return errors.New("TimestampMax condition not met")
}
counter := 0
for _, account := range o.KnownAccounts {
if account.StorageRoot != nil {
counter += 1
} else if account.StorageSlots != nil {
counter += len(account.StorageSlots)
}
}
if counter > MaxNumberOfEntries {
return errors.New("knownAccounts too large")
}
return o.CheckStorage(statedb)
}
func (o *TransactionOpts) CheckStorage(statedb *state.StateDB) error {
for address, accountStorage := range o.KnownAccounts {
if accountStorage.StorageRoot != nil {
rootHash := statedb.GetRoot(address)
if rootHash != *accountStorage.StorageRoot {
return errors.New("storage root hash condition not met")
}
} 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")
}
}
}
}
return nil
}