|
| 1 | +package arbitrum_types |
| 2 | + |
| 3 | +import ( |
| 4 | + "bytes" |
| 5 | + "encoding/json" |
| 6 | + "strings" |
| 7 | + |
| 8 | + "github.com/ethereum/go-ethereum/common" |
| 9 | + "github.com/ethereum/go-ethereum/common/hexutil" |
| 10 | + "github.com/ethereum/go-ethereum/core/state" |
| 11 | + "github.com/ethereum/go-ethereum/rpc" |
| 12 | + "github.com/pkg/errors" |
| 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 := statedb.StorageTrie(address) |
| 95 | + if trie == nil { |
| 96 | + return NewRejectedError("Storage trie not found for address key in knownAccounts option") |
| 97 | + } |
| 98 | + if trie.Hash() != *rootHashOrSlots.RootHash { |
| 99 | + return NewRejectedError("Storage root hash condition not met") |
| 100 | + } |
| 101 | + } else if len(rootHashOrSlots.SlotValue) > 0 { |
| 102 | + for slot, value := range rootHashOrSlots.SlotValue { |
| 103 | + stored := statedb.GetState(address, slot) |
| 104 | + if !bytes.Equal(stored.Bytes(), value.Bytes()) { |
| 105 | + return NewRejectedError("Storage slot value condition not met") |
| 106 | + } |
| 107 | + } |
| 108 | + } // else rootHashOrSlots.SlotValue is empty - ignore it and check the rest of conditions |
| 109 | + } |
| 110 | + return nil |
| 111 | +} |
0 commit comments