github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/arbitrum_types/txoptions.go (about)

     1  package arbitrum_types
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  	"github.com/tacshi/go-ethereum/common"
    10  	"github.com/tacshi/go-ethereum/common/hexutil"
    11  	"github.com/tacshi/go-ethereum/core/state"
    12  	"github.com/tacshi/go-ethereum/rpc"
    13  )
    14  
    15  type rejectedError struct {
    16  	msg string
    17  }
    18  
    19  func NewRejectedError(msg string) *rejectedError {
    20  	return &rejectedError{msg: msg}
    21  }
    22  func (e rejectedError) Error() string { return e.msg }
    23  func (rejectedError) ErrorCode() int  { return -32003 }
    24  
    25  type limitExceededError struct {
    26  	msg string
    27  }
    28  
    29  func NewLimitExceededError(msg string) *limitExceededError {
    30  	return &limitExceededError{msg: msg}
    31  }
    32  func (e limitExceededError) Error() string { return e.msg }
    33  func (limitExceededError) ErrorCode() int  { return -32005 }
    34  
    35  func WrapOptionsCheckError(err error, msg string) error {
    36  	wrappedMsg := func(e rpc.Error, msg string) string {
    37  		return strings.Join([]string{msg, e.Error()}, ":")
    38  	}
    39  	switch e := err.(type) {
    40  	case *rejectedError:
    41  		return NewRejectedError(wrappedMsg(e, msg))
    42  	case *limitExceededError:
    43  		return NewLimitExceededError(wrappedMsg(e, msg))
    44  	default:
    45  		return errors.Wrap(err, msg)
    46  	}
    47  }
    48  
    49  type RootHashOrSlots struct {
    50  	RootHash  *common.Hash
    51  	SlotValue map[common.Hash]common.Hash
    52  }
    53  
    54  func (r *RootHashOrSlots) UnmarshalJSON(data []byte) error {
    55  	var hash common.Hash
    56  	var err error
    57  	if err = json.Unmarshal(data, &hash); err == nil {
    58  		r.RootHash = &hash
    59  		return nil
    60  	}
    61  	return json.Unmarshal(data, &r.SlotValue)
    62  }
    63  
    64  func (r RootHashOrSlots) MarshalJSON() ([]byte, error) {
    65  	if r.RootHash != nil {
    66  		return json.Marshal(*r.RootHash)
    67  	}
    68  	return json.Marshal(r.SlotValue)
    69  }
    70  
    71  type ConditionalOptions struct {
    72  	KnownAccounts  map[common.Address]RootHashOrSlots `json:"knownAccounts"`
    73  	BlockNumberMin *hexutil.Uint64                    `json:"blockNumberMin,omitempty"`
    74  	BlockNumberMax *hexutil.Uint64                    `json:"blockNumberMax,omitempty"`
    75  	TimestampMin   *hexutil.Uint64                    `json:"timestampMin,omitempty"`
    76  	TimestampMax   *hexutil.Uint64                    `json:"timestampMax,omitempty"`
    77  }
    78  
    79  func (o *ConditionalOptions) Check(l1BlockNumber uint64, l2Timestamp uint64, statedb *state.StateDB) error {
    80  	if o.BlockNumberMin != nil && l1BlockNumber < uint64(*o.BlockNumberMin) {
    81  		return NewRejectedError("BlockNumberMin condition not met")
    82  	}
    83  	if o.BlockNumberMax != nil && l1BlockNumber > uint64(*o.BlockNumberMax) {
    84  		return NewRejectedError("BlockNumberMax condition not met")
    85  	}
    86  	if o.TimestampMin != nil && l2Timestamp < uint64(*o.TimestampMin) {
    87  		return NewRejectedError("TimestampMin condition not met")
    88  	}
    89  	if o.TimestampMax != nil && l2Timestamp > uint64(*o.TimestampMax) {
    90  		return NewRejectedError("TimestampMax condition not met")
    91  	}
    92  	for address, rootHashOrSlots := range o.KnownAccounts {
    93  		if rootHashOrSlots.RootHash != nil {
    94  			trie, err := statedb.StorageTrie(address)
    95  			if err != nil {
    96  				return err
    97  			}
    98  			if trie == nil {
    99  				return NewRejectedError("Storage trie not found for address key in knownAccounts option")
   100  			}
   101  			if trie.Hash() != *rootHashOrSlots.RootHash {
   102  				return NewRejectedError("Storage root hash condition not met")
   103  			}
   104  		} else if len(rootHashOrSlots.SlotValue) > 0 {
   105  			for slot, value := range rootHashOrSlots.SlotValue {
   106  				stored := statedb.GetState(address, slot)
   107  				if !bytes.Equal(stored.Bytes(), value.Bytes()) {
   108  					return NewRejectedError("Storage slot value condition not met")
   109  				}
   110  			}
   111  		} // else rootHashOrSlots.SlotValue is empty - ignore it and check the rest of conditions
   112  	}
   113  	return nil
   114  }