From 6e9593ab30f644c0b70a59d16fcc72ced449ab13 Mon Sep 17 00:00:00 2001 From: Unique Divine <51418232+Unique-Divine@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:32:05 +0200 Subject: [PATCH] perf(evm-keeper-precompile): implement sorted map for `k.precompiles` to remove dead code (#1996) * feat(omap): implement Ethereum address sorter for ordered map * perf: finalize implementation * refactor: remove dead code + changelog * docs: from PR self-review --------- Co-authored-by: Kevin Yang <5478483+k-yang@users.noreply.github.com> --- CHANGELOG.md | 1 + proto/eth/evm/v1/evm.proto | 6 +- x/common/omap/impl.go | 45 ++++- x/common/omap/omap.go | 130 ++++++++++---- x/common/omap/omap_test.go | 152 ++++++++++++----- x/evm/errors.go | 3 - x/evm/evm.pb.go | 209 ++++++++--------------- x/evm/keeper/evm_state.go | 2 - x/evm/keeper/grpc_query_test.go | 2 +- x/evm/keeper/keeper.go | 10 +- x/evm/keeper/msg_server.go | 18 +- x/evm/keeper/precompiles.go | 65 ++----- x/evm/params.go | 64 ------- x/evm/precompile/funtoken.go | 6 +- x/evm/precompile/funtoken_test.go | 6 +- x/evm/precompile/precompile.go | 17 ++ x/evm/precompile/precompile_test.go | 50 ++++++ x/oracle/keeper/ballot.go | 2 +- x/oracle/keeper/update_exchange_rates.go | 2 +- 19 files changed, 418 insertions(+), 372 deletions(-) create mode 100644 x/evm/precompile/precompile_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 175bc9482..6d139fce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1985](https://github.com/NibiruChain/nibiru/pull/1985) - feat(evm)!: Use atto denomination for the wei units in the EVM so that NIBI is "ether" to clients. Only micronibi (unibi) amounts can be transferred. All clients follow the constraint equation, 1 ether == 1 NIBI == 10^6 unibi == 10^18 wei. - [#1986](https://github.com/NibiruChain/nibiru/pull/1986) - feat(evm): Combine both account queries into "/eth.evm.v1.Query/EthAccount", accepting both nibi-prefixed Bech32 addresses and Ethereum-type hexadecimal addresses as input. - [#1989](https://github.com/NibiruChain/nibiru/pull/1989) - refactor(evm): simplify evm module address +- [#1996](https://github.com/NibiruChain/nibiru/pull/1996) - perf(evm-keeper-precompile): implement sorted map for `k.precompiles` to remove dead code - [#1997](https://github.com/NibiruChain/nibiru/pull/1997) - refactor(evm): Remove unnecessary params: "enable_call", "enable_create". #### Dapp modules: perp, spot, oracle, etc diff --git a/proto/eth/evm/v1/evm.proto b/proto/eth/evm/v1/evm.proto index a1d625b38..ef0ceced5 100644 --- a/proto/eth/evm/v1/evm.proto +++ b/proto/eth/evm/v1/evm.proto @@ -47,9 +47,9 @@ message Params { // allow_unprotected_txs defines if replay-protected (i.e non EIP155 // signed) transactions can be executed on the state machine. bool allow_unprotected_txs = 6; - // active_precompiles defines the slice of hex addresses of the precompiled - // contracts that are active - repeated string active_precompiles = 7; + // DEPRECATED: active_precompiles + // All precompiles present according to the VM are active. + reserved 7; // evm_channels is the list of channel identifiers from EVM compatible chains repeated string evm_channels = 8 [ (gogoproto.customname) = "EVMChannels" ]; diff --git a/x/common/omap/impl.go b/x/common/omap/impl.go index d7832dcdd..992ffa3bd 100644 --- a/x/common/omap/impl.go +++ b/x/common/omap/impl.go @@ -1,6 +1,10 @@ package omap import ( + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/NibiruChain/nibiru/x/common/asset" ) @@ -9,12 +13,12 @@ func stringIsLess(a, b string) bool { } // --------------------------------------------------------------------------- -// OrderedMap[string, V]: OrderedMap_String +// SortedMap[string, V] // --------------------------------------------------------------------------- -func OrderedMap_String[V any](data map[string]V) OrderedMap[string, V] { - omap := OrderedMap[string, V]{} - return omap.BuildFrom(data, stringSorter{}) +func SortedMap_String[V any](data map[string]V) SortedMap[string, V] { + omap := SortedMap[string, V]{} + return *omap.BuildFrom(data, stringSorter{}) } // stringSorter is a Sorter implementation for keys of type string . It uses @@ -28,14 +32,14 @@ func (sorter stringSorter) Less(a, b string) bool { } // --------------------------------------------------------------------------- -// OrderedMap[asset.Pair, V]: OrderedMap_Pair +// SortedMap[asset.Pair, V] // --------------------------------------------------------------------------- -func OrderedMap_Pair[V any]( +func SortedMap_Pair[V any]( data map[asset.Pair]V, -) OrderedMap[asset.Pair, V] { - omap := OrderedMap[asset.Pair, V]{} - return omap.BuildFrom(data, pairSorter{}) +) SortedMap[asset.Pair, V] { + omap := SortedMap[asset.Pair, V]{} + return *omap.BuildFrom(data, pairSorter{}) } // pairSorter is a Sorter implementation for keys of type asset.Pair. It uses @@ -47,3 +51,26 @@ var _ Sorter[asset.Pair] = (*pairSorter)(nil) func (sorter pairSorter) Less(a, b asset.Pair) bool { return stringIsLess(a.String(), b.String()) } + +// --------------------------------------------------------------------------- +// SortedMap[gethcommon.Address, V] +// --------------------------------------------------------------------------- + +func SortedMap_EthAddress[V any]( + data map[gethcommon.Address]V, +) SortedMap[gethcommon.Address, V] { + return *new(SortedMap[gethcommon.Address, V]).BuildFrom( + data, addrSorter{}, + ) +} + +// addrSorter implements "omap.Sorter" for the "gethcommon.Address" type. +type addrSorter struct{} + +var _ Sorter[gethcommon.Address] = (*addrSorter)(nil) + +func (s addrSorter) Less(a, b gethcommon.Address) bool { + aInt := new(big.Int).SetBytes(a.Bytes()) + bInt := new(big.Int).SetBytes(b.Bytes()) + return aInt.Cmp(bInt) < 0 +} diff --git a/x/common/omap/omap.go b/x/common/omap/omap.go index d40a3ed3c..e282e6ad4 100644 --- a/x/common/omap/omap.go +++ b/x/common/omap/omap.go @@ -1,9 +1,8 @@ // Package omap defines a generic-based type for creating ordered maps. It -// exports a "Sorter" interface, allowing the creation of ordered maps with +// exports a "Sorter" interface, allowing the creation of sorted maps with // custom key and value types. // -// Specifically, omap supports ordered maps with keys of type string or -// asset.Pair and values of any type. See impl.go for examples. +// See impl.go for examples. // // ## Motivation // @@ -19,14 +18,13 @@ import ( "sort" ) -// OrderedMap is a wrapper struct around the built-in map that has guarantees +// SortedMap is a wrapper struct around the built-in map that has guarantees // about order because it sorts its keys with a custom sorter. It has a public -// API that mirrors that functionality of `map`. OrderedMap is built with +// API that mirrors that functionality of `map`. SortedMap is built with // generics, so it can hold various combinations of key-value types. -type OrderedMap[K comparable, V any] struct { - Data map[K]V +type SortedMap[K comparable, V any] struct { + data map[K]V orderedKeys []K - keyIndexMap map[K]int // useful for delete operation isOrdered bool sorter Sorter[K] } @@ -38,11 +36,39 @@ type Sorter[K any] interface { Less(a K, b K) bool } +// SorterLeq is true if a <= b implements "less than or equal" using "Less" +func SorterLeq[K comparable](sorter Sorter[K], a, b K) bool { + return sorter.Less(a, b) || a == b +} + +// Data returns a copy of the underlying map (unordered, unsorted) +func (om *SortedMap[K, V]) Data() map[K]V { + dataCopy := make(map[K]V, len(om.data)) + for k, v := range om.InternalData() { + dataCopy[k] = v + } + return dataCopy +} + +// InternalData returns the SortedMap's private map. +func (om *SortedMap[K, V]) InternalData() map[K]V { + return om.data +} + +func (om *SortedMap[K, V]) Get(key K) (val V, exists bool) { + val, exists = om.data[key] + return val, exists +} + // ensureOrder is a method on the OrderedMap that sorts the keys in the map // and rebuilds the index map. -func (om *OrderedMap[K, V]) ensureOrder() { - keys := make([]K, 0, len(om.Data)) - for key := range om.Data { +func (om *SortedMap[K, V]) ensureOrder() { + if om.isOrdered { + return + } + + keys := make([]K, 0, len(om.data)) + for key := range om.data { keys = append(keys, key) } @@ -53,20 +79,16 @@ func (om *OrderedMap[K, V]) ensureOrder() { sort.Slice(keys, lessFunc) om.orderedKeys = keys - om.keyIndexMap = make(map[K]int) - for idx, key := range om.orderedKeys { - om.keyIndexMap[key] = idx - } om.isOrdered = true } // BuildFrom is a method that builds an OrderedMap from a given map and a // sorter for the keys. This function is useful for creating new OrderedMap // types with typed keys. -func (om OrderedMap[K, V]) BuildFrom( +func (om *SortedMap[K, V]) BuildFrom( data map[K]V, sorter Sorter[K], -) OrderedMap[K, V] { - om.Data = data +) *SortedMap[K, V] { + om.data = data om.sorter = sorter om.ensureOrder() return om @@ -76,7 +98,7 @@ func (om OrderedMap[K, V]) BuildFrom( // to iterate over the map in a deterministic order. Using a channel here // makes it so that the iteration is done lazily rather loading the entire // map (OrderedMap.data) into memory and then iterating. -func (om OrderedMap[K, V]) Range() <-chan (K) { +func (om *SortedMap[K, V]) Range() <-chan (K) { iterChan := make(chan K) go func() { defer close(iterChan) @@ -89,18 +111,18 @@ func (om OrderedMap[K, V]) Range() <-chan (K) { } // Has checks whether a key exists in the map. -func (om OrderedMap[K, V]) Has(key K) bool { - _, exists := om.Data[key] +func (om *SortedMap[K, V]) Has(key K) bool { + _, exists := om.data[key] return exists } // Len returns the number of items in the map. -func (om OrderedMap[K, V]) Len() int { - return len(om.Data) +func (om *SortedMap[K, V]) Len() int { + return len(om.data) } // Keys returns a slice of the keys in their sorted order. -func (om *OrderedMap[K, V]) Keys() []K { +func (om *SortedMap[K, V]) Keys() []K { if !om.isOrdered { om.ensureOrder() } @@ -109,19 +131,59 @@ func (om *OrderedMap[K, V]) Keys() []K { // Set adds a key-value pair to the map, or updates the value if the key // already exists. It ensures the keys are ordered after the operation. -func (om *OrderedMap[K, V]) Set(key K, val V) { - om.Data[key] = val +func (om *SortedMap[K, V]) Set(key K, val V) { + _, exists := om.data[key] + om.data[key] = val + + if !exists { + lenBefore := len(om.orderedKeys) + + if lenBefore == 0 { + // If the map is empty, create it. There's no need to search. + om.orderedKeys = []K{key} + return + } + + // If the key is new, insert it to the correctly sorted position + // Binary search works here and is in the standard library. + idx := sort.Search(lenBefore, func(i int) bool { + return om.sorter.Less(key, om.orderedKeys[i]) + }) + + // Update om.orderedKeys + newSortedKeys := make([]K, lenBefore+1) + front, back := om.orderedKeys[:idx], om.orderedKeys[idx:] + copy(newSortedKeys[:idx], front) // front + newSortedKeys[idx] = key // middle + copy(newSortedKeys[idx+1:], back) // back + om.orderedKeys = newSortedKeys + } +} + +// Union combines new key-value pairs into the ordered map. +func (om *SortedMap[K, V]) Union(kvMap map[K]V) { + for key, val := range kvMap { + om.data[key] = val + } + om.isOrdered = false om.ensureOrder() // TODO perf: make this more efficient with a clever insert. } // Delete removes a key-value pair from the map if the key exists. -func (om *OrderedMap[K, V]) Delete(key K) { - idx, keyExists := om.keyIndexMap[key] - if keyExists { - delete(om.Data, key) - - orderedKeys := om.orderedKeys - orderedKeys = append(orderedKeys[:idx], orderedKeys[idx+1:]...) - om.orderedKeys = orderedKeys +func (om *SortedMap[K, V]) Delete(key K) { + if _, keyExists := om.data[key]; keyExists { + lenBeforeDelete := om.Len() + delete(om.data, key) + + // Remove the key from orderedKeys while preserving the order + idx := sort.Search(lenBeforeDelete, func(i int) bool { + return SorterLeq(om.sorter, key, om.orderedKeys[i]) + }) + + // Update om.orderedKeys, skipping the deleted key (om.orderedKeys[idx]) + newSortedKeys := make([]K, lenBeforeDelete-1) + copy(newSortedKeys[:idx], om.orderedKeys[:idx]) // front + copy(newSortedKeys[idx:], om.orderedKeys[idx+1:]) // middle + back + om.orderedKeys = newSortedKeys } } diff --git a/x/common/omap/omap_test.go b/x/common/omap/omap_test.go index d0a8c0cbc..66559533c 100644 --- a/x/common/omap/omap_test.go +++ b/x/common/omap/omap_test.go @@ -2,18 +2,29 @@ package omap_test import ( "fmt" + "math/big" "sort" "testing" - "github.com/stretchr/testify/require" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/stretchr/testify/suite" "github.com/NibiruChain/nibiru/x/common/asset" "github.com/NibiruChain/nibiru/x/common/omap" ) -// TestLenHasKeys checks the length of the ordered map and verifies if the map +type Suite struct { + suite.Suite +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(Suite)) +} + +// TestLenHasKeys checks the length of the sorted map and verifies if the map // contains certain keys. -func TestLenHasKeys(t *testing.T) { +func (s *Suite) TestLenHasKeys() { type HasCheck struct { key string has bool @@ -45,70 +56,129 @@ func TestLenHasKeys(t *testing.T) { } for idx, tc := range testCases { - t.Run(fmt.Sprintf("case-%d", idx), func(t *testing.T) { - om := omap.OrderedMap_String[int](tc.dataMap) + s.Run(fmt.Sprintf("case-%d", idx), func() { + om := omap.SortedMap_String(tc.dataMap) - require.Equal(t, tc.len, om.Len()) + s.Require().Equal(tc.len, om.Len()) - orderedKeys := om.Keys() - definitelyOrderedKeys := []string{} - definitelyOrderedKeys = append(definitelyOrderedKeys, orderedKeys...) - sort.Strings(definitelyOrderedKeys) + sortedKeys := om.Keys() + definitelySortedKeys := []string{} + definitelySortedKeys = append(definitelySortedKeys, sortedKeys...) + sort.Strings(definitelySortedKeys) - require.Equal(t, definitelyOrderedKeys, orderedKeys) + s.Equal(definitelySortedKeys, sortedKeys) idx := 0 for key := range om.Range() { - require.Equal(t, orderedKeys[idx], key) + s.Equal(sortedKeys[idx], key) idx++ } }) } } -// TestGetSetDelete checks the Get, Set, and Delete operations on the OrderedMap. -func TestGetSetDelete(t *testing.T) { - om := omap.OrderedMap_String[string](make(map[string]string)) - require.Equal(t, 0, om.Len()) - - om.Set("foo", "fooval") - require.True(t, om.Has("foo")) - require.Equal(t, 1, om.Len()) - - om.Delete("bar") // shouldn't cause any problems - om.Delete("foo") - require.False(t, om.Has("foo")) - require.Equal(t, 0, om.Len()) +// TestGetSetDelete checks the Get, Set, and Delete operations on the SortedMap. +func (s *Suite) TestGetSetDelete() { + s.Run("single element", func() { + om := omap.SortedMap_String(make(map[string]string)) + s.Require().Equal(0, om.Len()) + om.Set("foo", "fooval") + s.Require().True(om.Has("foo")) + s.Require().Equal(1, om.Len()) + + om.Delete("bar") // shouldn't cause any problems + om.Delete("foo") + s.Require().False(om.Has("foo")) + s.Require().Equal(0, om.Len()) + }) + + s.Run("multiple elements", func() { + om := omap.SortedMap_String(map[string]string{ + "0": "w", + "1": "x", + "2": "y", + "3": "z", + }) + s.Require().Equal(4, om.Len()) + + om.Set("11", "xx") + om.Set("22", "yy") + s.Require().Equal([]string{"0", "1", "11", "2", "22", "3"}, om.Keys()) + + om.Delete("2") // shouldn't cause any problems + s.Require().False(om.Has("2")) + s.Require().Equal(5, om.Len()) + + s.Require().Equal([]string{"0", "1", "11", "22", "3"}, om.Keys()) + om.Union(map[string]string{"222": "", "111": ""}) + s.Require().Equal([]string{"0", "1", "11", "111", "22", "222", "3"}, om.Keys()) + + for k, v := range om.Data() { + gotVal, exists := om.Get(k) + s.True(exists) + s.Require().Equal(v, gotVal) + } + }) } -// TestOrderedMap_Pair tests an OrderedMap where the key is an asset.Pair, a -// type that isn't built-in. -func TestOrderedMap_Pair(t *testing.T) { +// DummyValue is a blank struct to use as a placeholder in the maps for the +// generic value argument. +type DummyValue struct{} + +// TestPair tests an SortedMap where the key is an asset.Pair. +func (s *Suite) TestPair() { pairStrs := []string{ "abc:xyz", "abc:abc", "aaa:bbb", "xyz:xyz", "bbb:ccc", "xyz:abc", } - orderedKeyStrs := []string{} - orderedKeyStrs = append(orderedKeyStrs, pairStrs...) - sort.Strings(orderedKeyStrs) + sortedKeyStrs := []string{} + sortedKeyStrs = append(sortedKeyStrs, pairStrs...) + sort.Strings(sortedKeyStrs) - orderedKeys := asset.MustNewPairs(orderedKeyStrs...) + sortedKeys := asset.MustNewPairs(sortedKeyStrs...) pairs := asset.MustNewPairs(pairStrs...) - type ValueType struct{} - unorderedMap := make(map[asset.Pair]ValueType) + unsortedMap := make(map[asset.Pair]DummyValue) for _, pair := range pairs { - unorderedMap[pair] = ValueType{} + unsortedMap[pair] = DummyValue{} } - om := omap.OrderedMap_Pair[ValueType](unorderedMap) - require.Equal(t, 6, om.Len()) - require.EqualValues(t, orderedKeys, om.Keys()) - require.NotEqualValues(t, asset.PairsToStrings(orderedKeys), pairStrs) + om := omap.SortedMap_Pair(unsortedMap) + s.Require().Equal(6, om.Len()) + s.Require().EqualValues(sortedKeys, om.Keys()) + s.Require().NotEqualValues(asset.PairsToStrings(sortedKeys), pairStrs) var pairsFromLoop []asset.Pair for pair := range om.Range() { pairsFromLoop = append(pairsFromLoop, pair) } - require.EqualValues(t, orderedKeys, pairsFromLoop) - require.NotEqualValues(t, pairsFromLoop, pairs) + s.Require().EqualValues(sortedKeys, pairsFromLoop) + s.Require().NotEqualValues(pairsFromLoop, pairs) +} + +func (s *Suite) TestEthAddress() { + s.Run("basic sorting", func() { + var sortedKeys []gethcommon.Address + unsortedMap := make(map[gethcommon.Address]DummyValue) + + // Prepare unsorted test inputs + omapKeyInt64s := []int64{1, 0, 4, 6, 3, 2, 5} + var unsortedKeys []gethcommon.Address + for _, i := range omapKeyInt64s { + bigInt := big.NewInt(i) + key := gethcommon.BigToAddress(bigInt) + unsortedKeys = append(unsortedKeys, key) + unsortedMap[key] = DummyValue{} + } + + { + for _, i := range []int64{0, 1, 2, 3, 4, 5, 6} { + sortedKeys = append(sortedKeys, gethcommon.BigToAddress(big.NewInt(i))) + } + } + + // Use sorter Sort + om := omap.SortedMap_EthAddress(unsortedMap) + s.Require().EqualValues(sortedKeys, om.Keys()) + s.NotEqualValues(unsortedKeys, sortedKeys) + }) } diff --git a/x/evm/errors.go b/x/evm/errors.go index 53bf9c6b4..a32facdcb 100644 --- a/x/evm/errors.go +++ b/x/evm/errors.go @@ -67,9 +67,6 @@ var ( // ErrInvalidGasLimit returns an error if gas limit value is invalid ErrInvalidGasLimit = errorsmod.Register(ModuleName, codeErrInvalidGasLimit, "invalid gas limit") - - // ErrInactivePrecompile returns an error if a call is made to an inactive precompile - ErrInactivePrecompile = errorsmod.Register(ModuleName, codeErrInactivePrecompile, "precompile not enabled") ) // NewExecErrorWithReason unpacks the revert return bytes and returns a wrapped error diff --git a/x/evm/evm.pb.go b/x/evm/evm.pb.go index 6b87f5c91..57d7943aa 100644 --- a/x/evm/evm.pb.go +++ b/x/evm/evm.pb.go @@ -97,9 +97,6 @@ type Params struct { // allow_unprotected_txs defines if replay-protected (i.e non EIP155 // signed) transactions can be executed on the state machine. AllowUnprotectedTxs bool `protobuf:"varint,6,opt,name=allow_unprotected_txs,json=allowUnprotectedTxs,proto3" json:"allow_unprotected_txs,omitempty"` - // active_precompiles defines the slice of hex addresses of the precompiled - // contracts that are active - ActivePrecompiles []string `protobuf:"bytes,7,rep,name=active_precompiles,json=activePrecompiles,proto3" json:"active_precompiles,omitempty"` // evm_channels is the list of channel identifiers from EVM compatible chains EVMChannels []string `protobuf:"bytes,8,rep,name=evm_channels,json=evmChannels,proto3" json:"evm_channels,omitempty"` // Fee deducted and burned when calling "CreateFunToken" in units of @@ -161,13 +158,6 @@ func (m *Params) GetAllowUnprotectedTxs() bool { return false } -func (m *Params) GetActivePrecompiles() []string { - if m != nil { - return m.ActivePrecompiles - } - return nil -} - func (m *Params) GetEVMChannels() []string { if m != nil { return m.EVMChannels @@ -647,79 +637,77 @@ func init() { func init() { proto.RegisterFile("eth/evm/v1/evm.proto", fileDescriptor_98abbdadb327b7d0) } var fileDescriptor_98abbdadb327b7d0 = []byte{ - // 1140 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x55, 0xcf, 0x6e, 0xdb, 0xc6, - 0x13, 0xb6, 0x2c, 0xca, 0xa2, 0x56, 0x4a, 0x44, 0xaf, 0x9d, 0xfc, 0xf8, 0x4b, 0x10, 0xd3, 0x60, - 0x2e, 0x0e, 0x90, 0x4a, 0xb5, 0x8b, 0x5e, 0x52, 0xb4, 0x80, 0xe5, 0xd8, 0x48, 0x54, 0x3b, 0x30, - 0x36, 0x4e, 0x0f, 0xbd, 0x10, 0x2b, 0x72, 0x4c, 0xb1, 0x22, 0xb9, 0x02, 0x77, 0xa9, 0xd2, 0x0f, - 0x50, 0xa0, 0xc7, 0x3e, 0x42, 0x4e, 0x7d, 0x96, 0xa0, 0xa7, 0x1c, 0x8b, 0x1e, 0x88, 0xc2, 0xb9, - 0xb4, 0x3a, 0xfa, 0xdc, 0x43, 0xb1, 0xbb, 0x94, 0xe5, 0xb8, 0x40, 0x4e, 0x9a, 0xef, 0x9b, 0x9d, - 0x3f, 0x3b, 0xf3, 0xad, 0x88, 0x36, 0x41, 0x8c, 0xfb, 0x30, 0x4b, 0xfa, 0xb3, 0x5d, 0xf9, 0xd3, - 0x9b, 0x66, 0x4c, 0x30, 0x8c, 0x40, 0x8c, 0x7b, 0x12, 0xce, 0x76, 0x1f, 0x6c, 0x86, 0x2c, 0x64, - 0x8a, 0xee, 0x4b, 0x4b, 0x9f, 0x70, 0x7f, 0xad, 0x21, 0xf3, 0x28, 0x4f, 0xcf, 0xd8, 0x04, 0x52, - 0x7c, 0x8a, 0x10, 0x64, 0xfe, 0xde, 0xe7, 0x1e, 0x0d, 0x82, 0xcc, 0xae, 0x6d, 0xd7, 0x76, 0x5a, - 0x83, 0xdd, 0x77, 0xa5, 0xb3, 0xf2, 0x47, 0xe9, 0x3c, 0x09, 0x23, 0x31, 0xce, 0x47, 0x3d, 0x9f, - 0x25, 0xfd, 0x57, 0xd1, 0x28, 0xca, 0xf2, 0x83, 0x31, 0x8d, 0xd2, 0x7e, 0xaa, 0xec, 0xbe, 0x2c, - 0xf4, 0x02, 0x8a, 0xfd, 0x20, 0xc8, 0x48, 0x4b, 0x25, 0x91, 0x26, 0x7e, 0x84, 0xd0, 0x88, 0xa6, - 0x13, 0x2f, 0x80, 0x94, 0x25, 0xf6, 0xaa, 0xcc, 0x48, 0x5a, 0x92, 0x79, 0x2e, 0x09, 0xfc, 0x04, - 0xad, 0x47, 0xdc, 0x4b, 0x68, 0x00, 0xde, 0x79, 0xc6, 0x12, 0xcf, 0x67, 0x51, 0x6a, 0xd7, 0xb7, - 0x6b, 0x3b, 0x26, 0xb9, 0x1b, 0xf1, 0x13, 0x1a, 0xc0, 0x51, 0xc6, 0x92, 0x03, 0x16, 0xa5, 0xee, - 0x3f, 0xab, 0x68, 0xed, 0x94, 0x66, 0x34, 0xe1, 0x78, 0x17, 0xb5, 0x60, 0x96, 0x54, 0x39, 0x75, - 0x97, 0x9b, 0x57, 0xa5, 0x63, 0x5d, 0xd0, 0x24, 0x7e, 0xe6, 0x5e, 0xbb, 0x5c, 0x62, 0xc2, 0x2c, - 0xd1, 0x85, 0xf6, 0x11, 0x82, 0x42, 0x64, 0xd4, 0x83, 0x68, 0xca, 0x6d, 0x63, 0xbb, 0xbe, 0x53, - 0x1f, 0xb8, 0x97, 0xa5, 0xd3, 0x3a, 0x94, 0xec, 0xe1, 0xcb, 0x53, 0x7e, 0x55, 0x3a, 0xeb, 0x55, - 0x82, 0xeb, 0x83, 0x2e, 0x69, 0x29, 0x70, 0x18, 0x4d, 0x39, 0xde, 0x43, 0xf7, 0x68, 0x1c, 0xb3, - 0x1f, 0xbd, 0x3c, 0x95, 0xa3, 0x03, 0x5f, 0x40, 0xe0, 0x89, 0x82, 0xdb, 0x6b, 0xaa, 0xdf, 0x0d, - 0xe5, 0x7c, 0xb3, 0xf4, 0x9d, 0x15, 0x1c, 0x7f, 0x86, 0x30, 0xf5, 0x45, 0x34, 0x03, 0x6f, 0x9a, - 0x81, 0xcf, 0x92, 0x69, 0x14, 0x03, 0xb7, 0x9b, 0xdb, 0xf5, 0x9d, 0x16, 0x59, 0xd7, 0x9e, 0xd3, - 0xa5, 0x03, 0xef, 0xa1, 0x8e, 0xec, 0xde, 0x1f, 0xd3, 0x34, 0x85, 0x98, 0xdb, 0xa6, 0x3c, 0x38, - 0xe8, 0x5e, 0x96, 0x4e, 0xfb, 0xf0, 0xbb, 0x93, 0x83, 0x8a, 0x26, 0x6d, 0x98, 0x25, 0x0b, 0x80, - 0x4f, 0xd0, 0x86, 0x9f, 0x01, 0x15, 0xe0, 0x9d, 0xe7, 0xa9, 0x90, 0x6b, 0xf4, 0xce, 0x01, 0xec, - 0x96, 0x1a, 0xcb, 0xa3, 0x6a, 0x79, 0xf7, 0x7c, 0xc6, 0x13, 0xc6, 0x79, 0x30, 0xe9, 0x45, 0xac, - 0x9f, 0x50, 0x31, 0xee, 0xbd, 0x4c, 0x05, 0x59, 0xd7, 0x91, 0x47, 0x55, 0xe0, 0x11, 0xc0, 0x33, - 0xe3, 0xaf, 0xb7, 0x4e, 0x6d, 0x68, 0x98, 0xab, 0x56, 0x7d, 0x68, 0x98, 0x75, 0xcb, 0x18, 0x1a, - 0x66, 0xc3, 0x5a, 0x73, 0xfb, 0xa8, 0xf1, 0x5a, 0x50, 0x01, 0xd8, 0x42, 0xf5, 0x09, 0x5c, 0xe8, - 0xb1, 0x13, 0x69, 0xe2, 0x4d, 0xd4, 0x98, 0xd1, 0x38, 0x87, 0x6a, 0xbd, 0x1a, 0xb8, 0x43, 0xd4, - 0x3d, 0xcb, 0x68, 0xca, 0xe5, 0x2d, 0x59, 0x7a, 0xcc, 0x42, 0x8e, 0x31, 0x32, 0xc6, 0x94, 0x8f, - 0xab, 0x58, 0x65, 0xe3, 0xc7, 0xc8, 0x88, 0x59, 0xc8, 0xed, 0xd5, 0xed, 0xfa, 0x4e, 0x7b, 0xaf, - 0xdb, 0x5b, 0x0a, 0xb6, 0x77, 0xcc, 0x42, 0xa2, 0x9c, 0xee, 0x6f, 0xab, 0xa8, 0x7e, 0xcc, 0x42, - 0x6c, 0xa3, 0xa6, 0x54, 0x26, 0x70, 0x5e, 0xe5, 0x58, 0x40, 0x7c, 0x1f, 0xad, 0x09, 0x36, 0x8d, - 0x7c, 0x9d, 0xa8, 0x45, 0x2a, 0x24, 0x4b, 0x06, 0x54, 0x50, 0xa5, 0xa9, 0x0e, 0x51, 0xb6, 0x9c, - 0xf2, 0x28, 0x66, 0xfe, 0xc4, 0x4b, 0xf3, 0x64, 0x04, 0x99, 0x6d, 0x6c, 0xd7, 0x76, 0x8c, 0x41, - 0x77, 0x5e, 0x3a, 0x6d, 0xc5, 0xbf, 0x52, 0x34, 0xb9, 0x09, 0xf0, 0x53, 0xd4, 0x14, 0x85, 0xa7, - 0xba, 0x6f, 0xa8, 0xc9, 0x6e, 0xcc, 0x4b, 0xa7, 0x2b, 0x96, 0x17, 0x7c, 0x41, 0xf9, 0x98, 0xac, - 0x89, 0x42, 0xfe, 0xe2, 0x3e, 0x32, 0x45, 0xe1, 0x45, 0x69, 0x00, 0x85, 0x52, 0x87, 0x31, 0xd8, - 0x9c, 0x97, 0x8e, 0x75, 0xe3, 0xf8, 0x4b, 0xe9, 0x23, 0x4d, 0x51, 0x28, 0x03, 0x3f, 0x45, 0x48, - 0xb7, 0xa4, 0x2a, 0x34, 0x55, 0x85, 0x3b, 0xf3, 0xd2, 0x69, 0x29, 0x56, 0xe5, 0x5e, 0x9a, 0xd8, - 0x45, 0x0d, 0x9d, 0xdb, 0x54, 0xb9, 0x3b, 0xf3, 0xd2, 0x31, 0x63, 0x16, 0xea, 0x9c, 0xda, 0x25, - 0x47, 0x95, 0x41, 0xc2, 0x66, 0x10, 0x28, 0x29, 0x98, 0x64, 0x01, 0xdd, 0x9f, 0x56, 0x91, 0x79, - 0x56, 0x10, 0xe0, 0x79, 0x2c, 0xf0, 0x11, 0xb2, 0x7c, 0x96, 0x8a, 0x8c, 0xfa, 0xc2, 0xfb, 0x68, - 0xb4, 0x83, 0x87, 0x57, 0xa5, 0xf3, 0x3f, 0xfd, 0x20, 0x6e, 0x9f, 0x70, 0x49, 0x77, 0x41, 0xed, - 0x57, 0xf3, 0xdf, 0x44, 0x8d, 0x51, 0xcc, 0xaa, 0x27, 0xde, 0x21, 0x1a, 0xe0, 0x63, 0x35, 0x35, - 0xb5, 0x5f, 0xb9, 0x80, 0xf6, 0xde, 0xc3, 0x9b, 0xfb, 0xbd, 0x25, 0x8f, 0xc1, 0x7d, 0x29, 0xd6, - 0xab, 0xd2, 0xb9, 0xab, 0xab, 0x56, 0x91, 0xae, 0x9c, 0xaa, 0x92, 0x8f, 0x85, 0xea, 0x19, 0x08, - 0xb5, 0xae, 0x0e, 0x91, 0x26, 0x7e, 0x80, 0xcc, 0x0c, 0x66, 0x90, 0x09, 0x08, 0xd4, 0x5a, 0x4c, - 0x72, 0x8d, 0xf1, 0xff, 0x91, 0x19, 0x52, 0xee, 0xe5, 0x1c, 0x02, 0xbd, 0x03, 0xd2, 0x0c, 0x29, - 0x7f, 0xc3, 0x21, 0x78, 0x66, 0xfc, 0xfc, 0xd6, 0x59, 0x71, 0x29, 0x6a, 0xef, 0xfb, 0x3e, 0x70, - 0x7e, 0x96, 0x4f, 0x63, 0xf8, 0x84, 0xb6, 0xf6, 0x50, 0x87, 0x0b, 0x96, 0xd1, 0x10, 0xbc, 0x09, - 0x5c, 0x54, 0x0a, 0xd3, 0x7a, 0xa9, 0xf8, 0x6f, 0xe1, 0x82, 0x93, 0x9b, 0xa0, 0x2a, 0xf1, 0x77, - 0x1d, 0xb5, 0xcf, 0x32, 0xea, 0xc3, 0x01, 0x4b, 0xcf, 0xa3, 0x50, 0xa9, 0x54, 0xc2, 0xea, 0xbf, - 0x95, 0x54, 0x48, 0xd6, 0x16, 0x51, 0x02, 0x2c, 0x17, 0xd5, 0x1b, 0x5a, 0x40, 0x19, 0x91, 0x01, - 0x14, 0xe0, 0xab, 0x01, 0x1a, 0xa4, 0x42, 0xf8, 0x4b, 0x74, 0x27, 0x88, 0x38, 0x1d, 0xc5, 0xe0, - 0x71, 0x41, 0xfd, 0x89, 0xbe, 0xfe, 0xc0, 0x9a, 0x97, 0x4e, 0xa7, 0x72, 0xbc, 0x96, 0x3c, 0xf9, - 0x08, 0xe1, 0xaf, 0x50, 0x77, 0x19, 0xa6, 0xba, 0xd5, 0xff, 0x5e, 0x03, 0x3c, 0x2f, 0x9d, 0xbb, - 0xd7, 0x47, 0x95, 0x87, 0xdc, 0xc2, 0x72, 0xc7, 0x01, 0x8c, 0xf2, 0x50, 0xc9, 0xce, 0x24, 0x1a, - 0x48, 0x36, 0x8e, 0x92, 0x48, 0x28, 0x99, 0x35, 0x88, 0x06, 0xb2, 0x3f, 0x48, 0x55, 0x9d, 0x04, - 0x12, 0x96, 0x5d, 0xd8, 0xed, 0x65, 0x7f, 0xda, 0x71, 0xa2, 0x78, 0xf2, 0x11, 0xc2, 0x03, 0x84, - 0xab, 0xb0, 0x0c, 0x44, 0x9e, 0xa5, 0x9e, 0x7a, 0xbc, 0x1d, 0x15, 0xab, 0x9e, 0x90, 0xf6, 0x12, - 0xe5, 0x7c, 0x4e, 0x05, 0x25, 0xff, 0x61, 0xf0, 0x37, 0x08, 0xeb, 0xb1, 0x7a, 0x3f, 0x70, 0x96, - 0x7a, 0xbe, 0x1a, 0xbd, 0x7d, 0x47, 0x89, 0x5a, 0xd5, 0xd7, 0x5e, 0xbd, 0x12, 0x62, 0x69, 0x34, - 0xe4, 0x2c, 0xd5, 0xcc, 0xd0, 0x30, 0x0d, 0xab, 0x31, 0x34, 0xcc, 0xa6, 0x65, 0x0e, 0x0d, 0x13, - 0x59, 0xed, 0xeb, 0x41, 0x54, 0x77, 0x21, 0x1b, 0x0b, 0x7c, 0xa3, 0xc9, 0xc1, 0xd7, 0xef, 0x2e, - 0xb7, 0x6a, 0xef, 0x2f, 0xb7, 0x6a, 0x7f, 0x5e, 0x6e, 0xd5, 0x7e, 0xf9, 0xb0, 0xb5, 0xf2, 0xfe, - 0xc3, 0xd6, 0xca, 0xef, 0x1f, 0xb6, 0x56, 0xbe, 0x7f, 0xfc, 0xe9, 0x2f, 0x67, 0x21, 0xbf, 0xd7, - 0xa3, 0x35, 0xf5, 0x39, 0xfe, 0xe2, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x78, 0xec, 0xe7, 0x69, - 0xc8, 0x07, 0x00, 0x00, + // 1115 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x55, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0xb6, 0x2c, 0xca, 0xa6, 0x56, 0x4a, 0xc4, 0xac, 0x9d, 0x94, 0x4d, 0x10, 0x53, 0x60, 0x2e, + 0x0a, 0x10, 0x48, 0xb5, 0x8b, 0x5e, 0x52, 0xb4, 0x80, 0xe5, 0xd8, 0x48, 0x54, 0x3b, 0x08, 0x36, + 0x4a, 0x0f, 0xbd, 0x10, 0x2b, 0x72, 0x4c, 0xb1, 0x22, 0xb9, 0x06, 0x77, 0xa9, 0xd2, 0x0f, 0x50, + 0xa0, 0xc7, 0x3e, 0x42, 0x4e, 0x7d, 0x96, 0xa0, 0xa7, 0x00, 0xbd, 0x14, 0x3d, 0x10, 0x85, 0x73, + 0x69, 0x75, 0xf4, 0x13, 0x14, 0xbb, 0x4b, 0x59, 0x8e, 0x0b, 0xe4, 0xc4, 0xf9, 0xbe, 0xd9, 0xf9, + 0xe1, 0xcc, 0xb7, 0x24, 0xda, 0x06, 0x31, 0x1d, 0xc0, 0x3c, 0x19, 0xcc, 0x77, 0xe5, 0xa3, 0x7f, + 0x96, 0x31, 0xc1, 0x30, 0x02, 0x31, 0xed, 0x4b, 0x38, 0xdf, 0xbd, 0xbf, 0x1d, 0xb2, 0x90, 0x29, + 0x7a, 0x20, 0x2d, 0x7d, 0xc2, 0xfd, 0xad, 0x86, 0xcc, 0xa3, 0x3c, 0x1d, 0xb3, 0x19, 0xa4, 0xf8, + 0x15, 0x42, 0x90, 0xf9, 0x7b, 0x5f, 0x78, 0x34, 0x08, 0x32, 0xbb, 0xd6, 0xad, 0xf5, 0x9a, 0xc3, + 0xdd, 0x77, 0xa5, 0xb3, 0xf6, 0x57, 0xe9, 0x3c, 0x0e, 0x23, 0x31, 0xcd, 0x27, 0x7d, 0x9f, 0x25, + 0x83, 0x97, 0xd1, 0x24, 0xca, 0xf2, 0x83, 0x29, 0x8d, 0xd2, 0x41, 0xaa, 0xec, 0x81, 0x2c, 0xf4, + 0x1c, 0x8a, 0xfd, 0x20, 0xc8, 0x48, 0x53, 0x25, 0x91, 0x26, 0x7e, 0x88, 0xd0, 0x84, 0xa6, 0x33, + 0x2f, 0x80, 0x94, 0x25, 0xf6, 0xba, 0xcc, 0x48, 0x9a, 0x92, 0x79, 0x26, 0x09, 0xfc, 0x18, 0xdd, + 0x89, 0xb8, 0x97, 0xd0, 0x00, 0xbc, 0xd3, 0x8c, 0x25, 0x9e, 0xcf, 0xa2, 0xd4, 0xae, 0x77, 0x6b, + 0x3d, 0x93, 0xdc, 0x8e, 0xf8, 0x09, 0x0d, 0xe0, 0x28, 0x63, 0xc9, 0x01, 0x8b, 0x52, 0xf7, 0x8f, + 0x75, 0xb4, 0xf1, 0x8a, 0x66, 0x34, 0xe1, 0x78, 0x17, 0x35, 0x61, 0x9e, 0x54, 0x39, 0x75, 0x97, + 0xdb, 0x97, 0xa5, 0x63, 0x9d, 0xd3, 0x24, 0x7e, 0xea, 0x5e, 0xb9, 0x5c, 0x62, 0xc2, 0x3c, 0xd1, + 0x85, 0xf6, 0x11, 0x82, 0x42, 0x64, 0xd4, 0x83, 0xe8, 0x8c, 0xdb, 0x46, 0xb7, 0xde, 0xab, 0x0f, + 0xdd, 0x8b, 0xd2, 0x69, 0x1e, 0x4a, 0xf6, 0xf0, 0xc5, 0x2b, 0x7e, 0x59, 0x3a, 0x77, 0xaa, 0x04, + 0x57, 0x07, 0x5d, 0xd2, 0x54, 0xe0, 0x30, 0x3a, 0xe3, 0x78, 0x0f, 0xdd, 0xa5, 0x71, 0xcc, 0x7e, + 0xf2, 0xf2, 0x54, 0x8e, 0x0e, 0x7c, 0x01, 0x81, 0x27, 0x0a, 0x6e, 0x6f, 0xa8, 0x7e, 0xb7, 0x94, + 0xf3, 0xcd, 0xca, 0x37, 0x2e, 0x64, 0x4c, 0x5b, 0xb6, 0xe3, 0x4f, 0x69, 0x9a, 0x42, 0xcc, 0x6d, + 0xb3, 0x5b, 0xef, 0x35, 0x87, 0x9d, 0x8b, 0xd2, 0x69, 0x1d, 0x7e, 0x7f, 0x72, 0x50, 0xd1, 0xa4, + 0x05, 0xf3, 0x64, 0x09, 0xf0, 0x09, 0xda, 0xf2, 0x33, 0xa0, 0x02, 0xbc, 0xd3, 0x3c, 0x15, 0x72, + 0x2f, 0xde, 0x29, 0x80, 0xdd, 0x54, 0xef, 0xf9, 0xb0, 0xda, 0xc6, 0x5d, 0x9f, 0xf1, 0x84, 0x71, + 0x1e, 0xcc, 0xfa, 0x11, 0x1b, 0x24, 0x54, 0x4c, 0xfb, 0x2f, 0x52, 0x41, 0xee, 0xe8, 0xc8, 0xa3, + 0x2a, 0xf0, 0x08, 0xe0, 0xa9, 0xf1, 0xcf, 0x5b, 0xa7, 0x36, 0x32, 0xcc, 0x75, 0xab, 0x3e, 0x32, + 0xcc, 0xba, 0x65, 0x8c, 0x0c, 0xb3, 0x61, 0x6d, 0x8c, 0x0c, 0x73, 0xd3, 0x32, 0xdd, 0x01, 0x6a, + 0xbc, 0x16, 0x54, 0x00, 0xb6, 0x50, 0x7d, 0x06, 0xe7, 0x7a, 0x9a, 0x44, 0x9a, 0x78, 0x1b, 0x35, + 0xe6, 0x34, 0xce, 0xa1, 0xda, 0x9a, 0x06, 0xee, 0x08, 0x75, 0xc6, 0x19, 0x4d, 0x39, 0xf5, 0x45, + 0xc4, 0xd2, 0x63, 0x16, 0x72, 0x8c, 0x91, 0x31, 0xa5, 0x7c, 0x5a, 0xc5, 0x2a, 0x1b, 0x3f, 0x42, + 0x46, 0xcc, 0x42, 0x6e, 0xaf, 0x77, 0xeb, 0xbd, 0xd6, 0x5e, 0xa7, 0xbf, 0xd2, 0x61, 0xff, 0x98, + 0x85, 0x44, 0x39, 0xdd, 0xdf, 0xd7, 0x51, 0xfd, 0x98, 0x85, 0xd8, 0x46, 0x9b, 0x52, 0x70, 0xc0, + 0x79, 0x95, 0x63, 0x09, 0xf1, 0x3d, 0xb4, 0x21, 0xd8, 0x59, 0xe4, 0xeb, 0x44, 0x4d, 0x52, 0x21, + 0x59, 0x32, 0xa0, 0x82, 0x2a, 0xa9, 0xb4, 0x89, 0xb2, 0xe5, 0xac, 0x27, 0x31, 0xf3, 0x67, 0x5e, + 0x9a, 0x27, 0x13, 0xc8, 0x6c, 0xa3, 0x5b, 0xeb, 0x19, 0xc3, 0xce, 0xa2, 0x74, 0x5a, 0x8a, 0x7f, + 0xa9, 0x68, 0x72, 0x1d, 0xe0, 0x27, 0x68, 0x53, 0x14, 0x9e, 0xea, 0xbe, 0xa1, 0xe6, 0xbb, 0xb5, + 0x28, 0x9d, 0x8e, 0x58, 0xbd, 0xe0, 0x73, 0xca, 0xa7, 0x64, 0x43, 0x14, 0xf2, 0x89, 0x07, 0xc8, + 0x14, 0x85, 0x17, 0xa5, 0x01, 0x14, 0x6a, 0xe9, 0xc6, 0x70, 0x7b, 0x51, 0x3a, 0xd6, 0xb5, 0xe3, + 0x2f, 0xa4, 0x8f, 0x6c, 0x8a, 0x42, 0x19, 0xf8, 0x09, 0x42, 0xba, 0x25, 0x55, 0x61, 0x53, 0x55, + 0xb8, 0xb5, 0x28, 0x9d, 0xa6, 0x62, 0x55, 0xee, 0x95, 0x89, 0x5d, 0xd4, 0xd0, 0xb9, 0x4d, 0x95, + 0xbb, 0xbd, 0x28, 0x1d, 0x33, 0x66, 0xa1, 0xce, 0xa9, 0x5d, 0x72, 0x54, 0x19, 0x24, 0x6c, 0x0e, + 0x81, 0x12, 0x84, 0x49, 0x96, 0xd0, 0xfd, 0x79, 0x1d, 0x99, 0xe3, 0x82, 0x00, 0xcf, 0x63, 0x81, + 0x8f, 0x90, 0xe5, 0xb3, 0x54, 0x64, 0xd4, 0x17, 0xde, 0x47, 0xa3, 0x1d, 0x3e, 0xb8, 0x2c, 0x9d, + 0xcf, 0xb4, 0xce, 0x6f, 0x9e, 0x70, 0x49, 0x67, 0x49, 0xed, 0x57, 0xf3, 0xdf, 0x46, 0x8d, 0x49, + 0xcc, 0xaa, 0x9b, 0xdb, 0x26, 0x1a, 0xe0, 0x63, 0x35, 0x35, 0xb5, 0x5f, 0xb9, 0x80, 0xd6, 0xde, + 0x83, 0xeb, 0xfb, 0xbd, 0x21, 0x8f, 0xe1, 0x3d, 0x29, 0xd9, 0xcb, 0xd2, 0xb9, 0xad, 0xab, 0x56, + 0x91, 0xae, 0x9c, 0xaa, 0x92, 0x8f, 0x85, 0xea, 0x19, 0x08, 0xb5, 0xae, 0x36, 0x91, 0x26, 0xbe, + 0x8f, 0xcc, 0x0c, 0xe6, 0x90, 0x09, 0x08, 0xd4, 0x5a, 0x4c, 0x72, 0x85, 0xf1, 0xe7, 0xc8, 0x0c, + 0x29, 0xf7, 0x72, 0x0e, 0x81, 0xde, 0x01, 0xd9, 0x0c, 0x29, 0x7f, 0xc3, 0x21, 0x78, 0x6a, 0xfc, + 0xf2, 0xd6, 0x59, 0x73, 0x29, 0x6a, 0xed, 0xfb, 0x3e, 0x70, 0x3e, 0xce, 0xcf, 0x62, 0xf8, 0x84, + 0xb6, 0xf6, 0x50, 0x9b, 0x0b, 0x96, 0xd1, 0x10, 0xbc, 0x19, 0x9c, 0x57, 0x0a, 0xd3, 0x7a, 0xa9, + 0xf8, 0xef, 0xe0, 0x9c, 0x93, 0xeb, 0xa0, 0x2a, 0xf1, 0x6f, 0x1d, 0xb5, 0xc6, 0x19, 0xf5, 0xe1, + 0x80, 0xa5, 0xa7, 0x51, 0xa8, 0x54, 0x2a, 0x61, 0xf5, 0xc9, 0x24, 0x15, 0x92, 0xb5, 0x45, 0x94, + 0x00, 0xcb, 0x45, 0x75, 0x87, 0x96, 0x50, 0x46, 0x64, 0x00, 0x05, 0xf8, 0x6a, 0x80, 0x06, 0xa9, + 0x10, 0xfe, 0x0a, 0xdd, 0x0a, 0x22, 0x4e, 0x27, 0x31, 0x78, 0x5c, 0x50, 0x7f, 0xa6, 0x5f, 0x7f, + 0x68, 0x2d, 0x4a, 0xa7, 0x5d, 0x39, 0x5e, 0x4b, 0x9e, 0x7c, 0x84, 0xf0, 0xd7, 0xa8, 0xb3, 0x0a, + 0x53, 0xdd, 0xea, 0x8f, 0xd2, 0x10, 0x2f, 0x4a, 0xe7, 0xf6, 0xd5, 0x51, 0xe5, 0x21, 0x37, 0xb0, + 0xdc, 0x71, 0x00, 0x93, 0x3c, 0x54, 0xb2, 0x33, 0x89, 0x06, 0x92, 0x8d, 0xa3, 0x24, 0x12, 0x4a, + 0x66, 0x0d, 0xa2, 0x81, 0xec, 0x0f, 0x52, 0x55, 0x27, 0x81, 0x84, 0x65, 0xe7, 0x76, 0x6b, 0xd5, + 0x9f, 0x76, 0x9c, 0x28, 0x9e, 0x7c, 0x84, 0xf0, 0x10, 0xe1, 0x2a, 0x2c, 0x03, 0x91, 0x67, 0xa9, + 0xa7, 0x2e, 0x6f, 0x5b, 0xc5, 0xaa, 0x2b, 0xa4, 0xbd, 0x44, 0x39, 0x9f, 0x51, 0x41, 0xc9, 0xff, + 0x18, 0xfc, 0x2d, 0xc2, 0x7a, 0xac, 0xde, 0x8f, 0x9c, 0xa5, 0x9e, 0xaf, 0x46, 0x6f, 0xdf, 0x52, + 0xa2, 0x56, 0xf5, 0xb5, 0x57, 0xaf, 0x84, 0x58, 0x1a, 0x8d, 0x38, 0x4b, 0x35, 0x33, 0x32, 0x4c, + 0xc3, 0x6a, 0xe8, 0xaf, 0xde, 0xc8, 0x30, 0x91, 0xd5, 0xba, 0x1a, 0x44, 0xf5, 0x2e, 0x64, 0x6b, + 0x89, 0xaf, 0x35, 0x39, 0xfc, 0xe6, 0xdd, 0xc5, 0x4e, 0xed, 0xfd, 0xc5, 0x4e, 0xed, 0xef, 0x8b, + 0x9d, 0xda, 0xaf, 0x1f, 0x76, 0xd6, 0xde, 0x7f, 0xd8, 0x59, 0xfb, 0xf3, 0xc3, 0xce, 0xda, 0x0f, + 0x8f, 0x3e, 0xfd, 0x43, 0x2c, 0xe4, 0x6f, 0x78, 0xb2, 0xa1, 0xfe, 0xb2, 0x5f, 0xfe, 0x17, 0x00, + 0x00, 0xff, 0xff, 0x46, 0x10, 0x1a, 0x96, 0x9f, 0x07, 0x00, 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -755,14 +743,6 @@ func (this *Params) Equal(that interface{}) bool { if this.AllowUnprotectedTxs != that1.AllowUnprotectedTxs { return false } - if len(this.ActivePrecompiles) != len(that1.ActivePrecompiles) { - return false - } - for i := range this.ActivePrecompiles { - if this.ActivePrecompiles[i] != that1.ActivePrecompiles[i] { - return false - } - } if len(this.EVMChannels) != len(that1.EVMChannels) { return false } @@ -865,15 +845,6 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x42 } } - if len(m.ActivePrecompiles) > 0 { - for iNdEx := len(m.ActivePrecompiles) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.ActivePrecompiles[iNdEx]) - copy(dAtA[i:], m.ActivePrecompiles[iNdEx]) - i = encodeVarintEvm(dAtA, i, uint64(len(m.ActivePrecompiles[iNdEx]))) - i-- - dAtA[i] = 0x3a - } - } if m.AllowUnprotectedTxs { i-- if m.AllowUnprotectedTxs { @@ -1340,12 +1311,6 @@ func (m *Params) Size() (n int) { if m.AllowUnprotectedTxs { n += 2 } - if len(m.ActivePrecompiles) > 0 { - for _, s := range m.ActivePrecompiles { - l = len(s) - n += 1 + l + sovEvm(uint64(l)) - } - } if len(m.EVMChannels) > 0 { for _, s := range m.EVMChannels { l = len(s) @@ -1825,38 +1790,6 @@ func (m *Params) Unmarshal(dAtA []byte) error { } } m.AllowUnprotectedTxs = bool(v != 0) - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ActivePrecompiles", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvm - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthEvm - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthEvm - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ActivePrecompiles = append(m.ActivePrecompiles, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EVMChannels", wireType) diff --git a/x/evm/keeper/evm_state.go b/x/evm/keeper/evm_state.go index 9045b1c1d..34a6c8eb0 100644 --- a/x/evm/keeper/evm_state.go +++ b/x/evm/keeper/evm_state.go @@ -3,7 +3,6 @@ package keeper import ( "math/big" - "slices" "github.com/NibiruChain/collections" "github.com/cosmos/cosmos-sdk/codec" @@ -118,7 +117,6 @@ func (k Keeper) GetParams(ctx sdk.Context) (params evm.Params) { // SetParams: Setter for the module parameters. func (k Keeper) SetParams(ctx sdk.Context, params evm.Params) { - slices.Sort(params.ActivePrecompiles) k.EvmState.ModuleParams.Set(ctx, params) } diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 90764d84b..cb697b45d 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -438,7 +438,7 @@ func (s *Suite) TestQueryParams() { s.Require().True(want.Equal(got), "want %s, got %s", want, got) // Empty params to test the setter - want.ActivePrecompiles = []string{"new", "something"} + want.EvmDenom = "wei" deps.EvmKeeper.SetParams(deps.Ctx, want) gotResp, err = deps.EvmKeeper.Params(deps.GoCtx(), nil) s.Require().NoError(err) diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index debdddfb5..70902c6a8 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -18,6 +18,7 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/NibiruChain/nibiru/app/appconst" + "github.com/NibiruChain/nibiru/x/common/omap" "github.com/NibiruChain/nibiru/x/evm" ) @@ -43,9 +44,12 @@ type Keeper struct { accountKeeper evm.AccountKeeper stakingKeeper evm.StakingKeeper - // Integer for the Ethereum EIP155 Chain ID - // eip155ChainIDInt *big.Int - precompiles map[gethcommon.Address]vm.PrecompiledContract //nolint:unused + // precompiles is the set of active precompiled contracts used in the EVM. + // Precompiles are special, built-in contract interfaces that exist at + // predefined address and run custom logic outside of what is possible only + // in Solidity. + precompiles omap.SortedMap[gethcommon.Address, vm.PrecompiledContract] + // tracer: Configures the output type for a geth `vm.EVMLogger`. Tracer types // include "access_list", "json", "struct", and "markdown". If any other // value is used, a no operation tracer is set. diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 803b785f8..9eaf25c63 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "math/big" - "slices" "strconv" "cosmossdk.io/errors" @@ -255,7 +254,7 @@ func (k *Keeper) NewEVM( } vmConfig := k.VMConfig(ctx, msg, evmConfig, tracer) theEvm := vm.NewEVM(blockCtx, txCtx, stateDB, evmConfig.ChainConfig, vmConfig) - theEvm.WithPrecompiles(k.precompiles, k.PrecompileAddrsSorted()) + theEvm.WithPrecompiles(k.precompiles.InternalData(), k.precompiles.Keys()) return theEvm } @@ -368,21 +367,6 @@ func (k *Keeper) ApplyEvmMsg(ctx sdk.Context, stateDB := statedb.New(ctx, k, txConfig) evmObj := k.NewEVM(ctx, msg, evmConfig, tracer, stateDB) - numPrecompiles := len(k.precompiles) - precompileAddrs := make([]gethcommon.Address, numPrecompiles) - - // Check if the transaction is sent to an inactive precompile - // - // NOTE: This has to be checked here instead of in the actual evm.Call method - // because evm.WithPrecompiles only populates the EVM with the active precompiles, - // so there's no telling if the To address is an inactive precompile further down the call stack. - toAddr := msg.To() - if toAddr != nil && - slices.Contains(evm.AvailableEVMExtensions, toAddr.String()) && - !slices.Contains(precompileAddrs, *toAddr) { - return nil, errors.Wrap(evm.ErrInactivePrecompile, "failed to call precompile") - } - leftoverGas := msg.Gas() // Allow the tracer captures the tx level events, mainly the gas consumption. diff --git a/x/evm/keeper/precompiles.go b/x/evm/keeper/precompiles.go index a972c7915..9326c30dd 100644 --- a/x/evm/keeper/precompiles.go +++ b/x/evm/keeper/precompiles.go @@ -2,36 +2,25 @@ package keeper import ( - "bytes" - "sort" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/x/common/omap" ) -// PrecompileSet is the set of all known precompile addresses. It includes defaults -// from go-ethereum and the custom ones specific to the Nibiru EVM. -func (k Keeper) PrecompileSet() set.Set[gethcommon.Address] { - precompiles := set.New[gethcommon.Address]() - for addr := range k.precompiles { - precompiles.Add(addr) - } - return precompiles -} - -func (k *Keeper) AddPrecompiles(precompileMap map[gethcommon.Address]vm.PrecompiledContract) { - if len(k.precompiles) == 0 { - k.precompiles = make(map[gethcommon.Address]vm.PrecompiledContract) - } - for addr, precompile := range precompileMap { - k.precompiles[addr] = precompile +func (k *Keeper) AddPrecompiles( + precompileMap map[gethcommon.Address]vm.PrecompiledContract, +) { + if k.precompiles.Len() == 0 { + newPrecompileMap := omap.SortedMap_EthAddress[vm.PrecompiledContract]( + precompileMap, + ) + k.precompiles = newPrecompileMap + } else { + for addr, precompile := range precompileMap { + k.precompiles.Set(addr, precompile) + } } - // The following TODOs can go in an epic together. - - // TODO: feat(evm): implement precompiled contracts for fungible tokens - // https://github.com/NibiruChain/nibiru/issues/1898 // TODO: feat(evm): implement precompiled contracts for ibc transfer // Check if there is sufficient demand for this. @@ -48,31 +37,5 @@ func (k *Keeper) AddPrecompiles(precompileMap map[gethcommon.Address]vm.Precompi // IsAvailablePrecompile returns true if the given precompile address is contained in the // EVM keeper's available precompiles map. func (k Keeper) IsAvailablePrecompile(address gethcommon.Address) bool { - _, ok := k.precompiles[address] - return ok -} - -// PrecompileAddrsSorted returns the list of available precompile addresses. -// -// NOTE: uses index based approach instead of append because it's supposed to be faster. -// Check https://stackoverflow.com/questions/21362950/getting-a-slice-of-keys-from-a-map. -// -// TODO: refactor(evm/keeper/precompiles): Use ordered map as the underlying -// struct to remove the need for iterating over k.precompiles in so many -// different ways. The set could also be tracked as well to make it ea -func (k Keeper) PrecompileAddrsSorted() []gethcommon.Address { - addresses := make([]gethcommon.Address, len(k.precompiles)) - i := 0 - - //#nosec G705 -- two operations in for loop here are fine - for address := range k.precompiles { - addresses[i] = address - i++ - } - - sort.Slice(addresses, func(i, j int) bool { - return bytes.Compare(addresses[i].Bytes(), addresses[j].Bytes()) == -1 - }) - - return addresses + return k.precompiles.Has(address) } diff --git a/x/evm/params.go b/x/evm/params.go index 07113fa82..74e85a9f6 100644 --- a/x/evm/params.go +++ b/x/evm/params.go @@ -3,8 +3,6 @@ package evm import ( "fmt" - "sort" - "strings" errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" @@ -12,12 +10,10 @@ import ( channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "golang.org/x/exp/slices" "github.com/NibiruChain/nibiru/app/appconst" - "github.com/NibiruChain/nibiru/eth" ) const ( @@ -25,19 +21,13 @@ const ( DefaultEVMDenom = appconst.BondDenom ) -// AvailableEVMExtensions defines the default active precompiles -var AvailableEVMExtensions = []string{} - // DefaultParams returns default evm parameters // ExtraEIPs is empty to prevent overriding the latest hard fork instruction set -// ActivePrecompiles is empty to prevent overriding the default precompiles -// from the EVM configuration. func DefaultParams() Params { return Params{ EvmDenom: DefaultEVMDenom, ExtraEIPs: []int64{}, AllowUnprotectedTxs: false, - ActivePrecompiles: AvailableEVMExtensions, EVMChannels: []string{}, CreateFuntokenFee: math.NewIntWithDecimal(10_000, 6), // 10_000 NIBI } @@ -75,10 +65,6 @@ func (p Params) Validate() error { return err } - if err := ValidatePrecompiles(p.ActivePrecompiles); err != nil { - return err - } - return validateChannels(p.EVMChannels) } @@ -91,32 +77,12 @@ func (p Params) EIPs() []int { return eips } -// GetActivePrecompilesAddrs is a util function that the Active Precompiles -// as a slice of addresses. -func (p Params) GetActivePrecompilesAddrs() []common.Address { - precompiles := make([]common.Address, len(p.ActivePrecompiles)) - for i, precompile := range p.ActivePrecompiles { - precompiles[i] = common.HexToAddress(precompile) - } - return precompiles -} - // IsEVMChannel returns true if the channel provided is in the list of // EVM channels func (p Params) IsEVMChannel(channel string) bool { return slices.Contains(p.EVMChannels, channel) } -// IsActivePrecompile returns true if the given precompile address is -// registered as an active precompile. -func (p Params) IsActivePrecompile(address string) bool { - _, found := sort.Find(len(p.ActivePrecompiles), func(i int) int { - return strings.Compare(address, p.ActivePrecompiles[i]) - }) - - return found -} - func validateEVMDenom(i interface{}) error { denom, ok := i.(string) if !ok { @@ -155,33 +121,3 @@ func validateEIPs(i interface{}) error { return nil } - -// ValidatePrecompiles checks if the precompile addresses are valid and unique. -func ValidatePrecompiles(i interface{}) error { - precompiles, ok := i.([]string) - if !ok { - return fmt.Errorf("invalid precompile slice type: %T", i) - } - - seenPrecompiles := make(map[string]struct{}) - for _, precompile := range precompiles { - if _, ok := seenPrecompiles[precompile]; ok { - return fmt.Errorf("duplicate precompile %s", precompile) - } - - if err := eth.ValidateAddress(precompile); err != nil { - return fmt.Errorf("invalid precompile %s", precompile) - } - - seenPrecompiles[precompile] = struct{}{} - } - - // NOTE: Check that the precompiles are sorted. This is required for the - // precompiles to be found correctly when using the IsActivePrecompile method, - // because of the use of sort.Find. - if !slices.IsSorted(precompiles) { - return fmt.Errorf("precompiles need to be sorted: %s", precompiles) - } - - return nil -} diff --git a/x/evm/precompile/funtoken.go b/x/evm/precompile/funtoken.go index 7a974ae31..395703d50 100644 --- a/x/evm/precompile/funtoken.go +++ b/x/evm/precompile/funtoken.go @@ -35,7 +35,11 @@ func (p precompileFunToken) Address() gethcommon.Address { } func (p precompileFunToken) RequiredGas(input []byte) (gasPrice uint64) { - // TODO: UD-DEBUG: not implemented yet. Currently set to 0 gasPrice + // TODO: https://github.com/NibiruChain/nibiru/issues/1990 + // We need to determine an appropriate gas value for the transaction to + // configure this function and add a assertions around the gas usage to + // the precompile's test suite. UD-DEBUG: not implemented yet. Currently + // set to 0 gasPrice return 22 } diff --git a/x/evm/precompile/funtoken_test.go b/x/evm/precompile/funtoken_test.go index c718c9b10..f3e26cb51 100644 --- a/x/evm/precompile/funtoken_test.go +++ b/x/evm/precompile/funtoken_test.go @@ -19,7 +19,7 @@ type Suite struct { } // TestPrecompileSuite: Runs all the tests in the suite. -func TestPrecompileSuite(t *testing.T) { +func TestSuite(t *testing.T) { s := new(Suite) suite.Run(t, s) } @@ -45,7 +45,7 @@ func (s *Suite) FunToken_PrecompileExists() { s.NoError(err) s.Equal(string(codeResp.Code), "") - s.True(deps.EvmKeeper.PrecompileSet().Has(precompileAddr.ToAddr()), + s.True(deps.EvmKeeper.IsAvailablePrecompile(precompileAddr.ToAddr()), "did not see precompile address during \"InitPrecompiles\"") callArgs := []any{"nonsense", "args here", "to see if", "precompile is", "called"} @@ -77,7 +77,7 @@ func (s *Suite) FunToken_HappyPath() { theUser := deps.Sender.EthAddr theEvm := evm.EVM_MODULE_ADDRESS - s.True(deps.EvmKeeper.PrecompileSet().Has(precompileAddr.ToAddr()), + s.True(deps.EvmKeeper.IsAvailablePrecompile(precompileAddr.ToAddr()), "did not see precompile address during \"InitPrecompiles\"") s.T().Log("Create FunToken mapping and ERC20") diff --git a/x/evm/precompile/precompile.go b/x/evm/precompile/precompile.go index ba9297440..7c874ed4d 100644 --- a/x/evm/precompile/precompile.go +++ b/x/evm/precompile/precompile.go @@ -1,3 +1,17 @@ +// Package precompile implements custom precompiles for the Nibiru EVM. +// +// Precompiles are special, built-in contract interfaces that exist at +// predefined addresses and run custom logic outside of what is possible +// in standard Solidity contracts. This package extends the default Ethereum +// precompiles with Nibiru-specific functionality. +// +// Key components: +// - InitPrecompiles: Initializes and returns a map of precompiled contracts. +// - NibiruPrecompile: Interface for Nibiru-specific precompiles. +// - PrecompileFunToken: Implements the FunToken precompile for ERC20-to-bank transfers. +// +// The package also provides utility functions for working with precompiles, such +// as "ABIMethodByID" and "OnRunStart" for common precompile execution setup. package precompile import ( @@ -78,7 +92,10 @@ func addPrecompileToVM(p vm.PrecompiledContract) { // vm.PrecompiledAddressesCancun, } +// NibiruPrecompile is the interface that all Nibiru-specific precompiles +// must implement. type NibiruPrecompile interface { + vm.PrecompiledContract ABI() *gethabi.ABI } diff --git a/x/evm/precompile/precompile_test.go b/x/evm/precompile/precompile_test.go new file mode 100644 index 000000000..387820687 --- /dev/null +++ b/x/evm/precompile/precompile_test.go @@ -0,0 +1,50 @@ +package precompile_test + +import ( + "math/big" + "sort" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/x/common/omap" + "github.com/NibiruChain/nibiru/x/evm/evmtest" + "github.com/NibiruChain/nibiru/x/evm/precompile" +) + +// This test proves that: +// 1. The VM precompiles are ordered +// 2. The output map from InitPrecompiles has the same addresses as the slice +// of VM precompile addresses +// 3. The ordered map produces the same ordered address slice. +// +// The VM precompiles are expected to be sorted. You'll notice from reading +// the constants for precompile addresses imported from +// "github.com/ethereum/go-ethereum/core/vm" that the order goes 1, 2, 3, ... +func (s *Suite) TestOrderedPrecompileAddresses() { + s.T().Log("1 | Prepare test inputs from output of \"precompile.InitPrecompiles\"") + // Types are written out to make the test easier to read. + // Note that orderedKeys must be set after InitPrecompiles to mirror the + // behavior of the Nibiru BaseApp. + deps := evmtest.NewTestDeps() + var unorderedMap map[gethcommon.Address]vm.PrecompiledContract = precompile.InitPrecompiles(deps.Chain.PublicKeepers) + var orderedKeys []gethcommon.Address = vm.PrecompiledAddressesBerlin + + s.T().Log("2 | Compute ordered keys from VM") + var vmAddrInts []*big.Int + var vmAddrIntsBefore []*big.Int // unchanged copy of vmAddrInts + for _, addr := range vm.PrecompiledAddressesBerlin { + vmAddrInt := new(big.Int).SetBytes(addr.Bytes()) + vmAddrInts = append(vmAddrInts, vmAddrInt) + vmAddrIntsBefore = append(vmAddrIntsBefore, vmAddrInt) + } + lessFunc := func(i, j int) bool { + return vmAddrInts[i].Cmp(vmAddrInts[j]) < 0 + } + sort.Slice(vmAddrInts, lessFunc) + s.Require().EqualValues(vmAddrInts, vmAddrIntsBefore, "vm precompiles not ordered in InitPrecompiles") + + s.T().Log("3 | The ordered map produces the same ordered address slice") + om := omap.SortedMap_EthAddress[vm.PrecompiledContract](unorderedMap) + s.Require().EqualValues(orderedKeys, om.Keys()) +} diff --git a/x/oracle/keeper/ballot.go b/x/oracle/keeper/ballot.go index bf873c7c5..0e0adc8de 100644 --- a/x/oracle/keeper/ballot.go +++ b/x/oracle/keeper/ballot.go @@ -118,7 +118,7 @@ func (k Keeper) removeInvalidVotes( ) // Iterate through sorted keys for deterministic ordering. - orderedPairVotes := omap.OrderedMap_Pair[types.ExchangeRateVotes](pairVotes) + orderedPairVotes := omap.SortedMap_Pair[types.ExchangeRateVotes](pairVotes) for pair := range orderedPairVotes.Range() { // If pair is not whitelisted, or the votes for it has failed, then skip // and remove it from pairBallotsMap for iteration efficiency diff --git a/x/oracle/keeper/update_exchange_rates.go b/x/oracle/keeper/update_exchange_rates.go index dff65f9dc..5d0eb2924 100644 --- a/x/oracle/keeper/update_exchange_rates.go +++ b/x/oracle/keeper/update_exchange_rates.go @@ -86,7 +86,7 @@ func (k Keeper) tallyVotesAndUpdatePrices( ) { rewardBand := k.RewardBand(ctx) // Iterate through sorted keys for deterministic ordering. - orderedPairVotes := omap.OrderedMap_Pair[types.ExchangeRateVotes](pairVotes) + orderedPairVotes := omap.SortedMap_Pair[types.ExchangeRateVotes](pairVotes) for pair := range orderedPairVotes.Range() { exchangeRate := Tally(pairVotes[pair], rewardBand, validatorPerformances) k.SetPrice(ctx, pair, exchangeRate)