diff --git a/cmd/rpc/query.go b/cmd/rpc/query.go index 5875f2de..2a13cd85 100644 --- a/cmd/rpc/query.go +++ b/cmd/rpc/query.go @@ -284,11 +284,20 @@ func (s *Server) Order(w http.ResponseWriter, r *http.Request, _ httprouter.Para // Orders retrieves the order book for a committee with optional filters and pagination func (s *Server) Orders(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { // Invoke helper with the HTTP request, response writer and an inline callback - s.ordersParams(w, r, func(s *fsm.StateMachine, req *ordersRequest) (any, lib.ErrorI) { + s.ordersParams(w, r, func(sm *fsm.StateMachine, req *ordersRequest) (any, lib.ErrorI) { // validate mutual exclusion: cannot filter by both seller and buyer address if req.SellersSendAddress != "" && req.BuyerSendAddress != "" { return nil, lib.NewError(lib.CodeInvalidArgument, lib.RPCModule, "cannot filter by both sellersSendAddress and buyerSendAddress") } + // validate that address-based queries are only allowed when indexing is enabled + if !sm.Config.StateMachineConfig.IndexOrdersByAddresses { + if req.SellersSendAddress != "" { + return nil, lib.NewError(lib.CodeInvalidArgument, lib.RPCModule, "sellersSendAddress filtering is disabled; set indexOrdersByAddresses=true in config") + } + if req.BuyerSendAddress != "" { + return nil, lib.NewError(lib.CodeInvalidArgument, lib.RPCModule, "buyerSendAddress filtering is disabled; set indexOrdersByAddresses=true in config") + } + } // convert seller address if provided var sellerAddr []byte if req.SellersSendAddress != "" { @@ -308,7 +317,7 @@ func (s *Server) Orders(w http.ResponseWriter, r *http.Request, _ httprouter.Par } } // use paginated query - return s.GetOrdersPaginated(sellerAddr, buyerAddr, req.Committee, req.PageParams) + return sm.GetOrdersPaginated(sellerAddr, buyerAddr, req.Committee, req.PageParams) }) } diff --git a/fsm/swap.go b/fsm/swap.go index f97dbdd8..f65c81b9 100644 --- a/fsm/swap.go +++ b/fsm/swap.go @@ -276,21 +276,28 @@ func (s *StateMachine) SetOrder(order *lib.SellOrder, chainId uint64) (err lib.E if err = s.Set(KeyForOrder(chainId, order.Id), protoBytes); err != nil { return } - // set the secondary index for seller address lookup (value is empty, key is sufficient) - if err = s.Set(KeyForOrderBySeller(order.SellersSendAddress, chainId, order.Id), []byte{}); err != nil { - return - } - // set the secondary index for buyer address lookup if buyer exists (locked order) - if len(order.BuyerSendAddress) > 0 { - if err = s.Set(KeyForOrderByBuyer(order.BuyerSendAddress, chainId, order.Id), []byte{}); err != nil { + // set secondary indexes only if configured + if s.Config.StateMachineConfig.IndexOrdersByAddresses { + // set the secondary index for seller address lookup (value is empty, key is sufficient) + if err = s.Set(KeyForOrderBySeller(order.SellersSendAddress, chainId, order.Id), []byte{}); err != nil { return } + // set the secondary index for buyer address lookup if buyer exists (locked order) + if len(order.BuyerSendAddress) > 0 { + if err = s.Set(KeyForOrderByBuyer(order.BuyerSendAddress, chainId, order.Id), []byte{}); err != nil { + return + } + } } return } // cleanupStaleBuyerIndex() removes the old buyer index entry if the buyer changed or was removed func (s *StateMachine) cleanupStaleBuyerIndex(order *lib.SellOrder, chainId uint64) lib.ErrorI { + // skip if indexing is disabled + if !s.Config.StateMachineConfig.IndexOrdersByAddresses { + return nil + } // check if order already exists existingOrder, err := s.GetOrder(order.Id, chainId) // if order not found, nothing to clean up @@ -321,15 +328,18 @@ func (s *StateMachine) DeleteOrder(orderId []byte, chainId uint64) (err lib.Erro if err = s.Delete(KeyForOrder(chainId, orderId)); err != nil { return } - // delete the seller secondary index entry - if err = s.Delete(KeyForOrderBySeller(order.SellersSendAddress, chainId, orderId)); err != nil { - return - } - // delete the buyer secondary index entry if buyer exists - if len(order.BuyerSendAddress) > 0 { - if err = s.Delete(KeyForOrderByBuyer(order.BuyerSendAddress, chainId, orderId)); err != nil { + // delete secondary indexes only if configured + if s.Config.StateMachineConfig.IndexOrdersByAddresses { + // delete the seller secondary index entry + if err = s.Delete(KeyForOrderBySeller(order.SellersSendAddress, chainId, orderId)); err != nil { return } + // delete the buyer secondary index entry if buyer exists + if len(order.BuyerSendAddress) > 0 { + if err = s.Delete(KeyForOrderByBuyer(order.BuyerSendAddress, chainId, orderId)); err != nil { + return + } + } } return } diff --git a/lib/config.go b/lib/config.go index 1579f816..54ba35da 100644 --- a/lib/config.go +++ b/lib/config.go @@ -158,21 +158,25 @@ const ( DefaultInitialTokensPerBlock = uint64(80 * 1000000) // 80 CNPY // the number of blocks between each halvening (block reward is cut in half) event DefaultBlocksPerHalvening = uint64(3150000) // ~ 2 years - 20 second blocks + // whether to index orders by seller/buyer address for efficient lookups + DefaultIndexOrdersByAddresses = true ) // StateMachineConfig houses FSM level options type StateMachineConfig struct { - InitialTokensPerBlock uint64 `json:"initialTokensPerBlock"` // initial micro tokens minted per block (before halvenings) - BlocksPerHalvening uint64 `json:"blocksPerHalvening"` // number of blocks between block reward halvings - FaucetAddress string `json:"faucetAddress"` // if set: "send" txs from this address will auto-mint on insufficient funds (dev/test only) + InitialTokensPerBlock uint64 `json:"initialTokensPerBlock"` // initial micro tokens minted per block (before halvenings) + BlocksPerHalvening uint64 `json:"blocksPerHalvening"` // number of blocks between block reward halvings + FaucetAddress string `json:"faucetAddress"` // if set: "send" txs from this address will auto-mint on insufficient funds (dev/test only) + IndexOrdersByAddresses bool `json:"indexOrdersByAddresses"` // index orders by seller/buyer address for efficient lookups } // DefaultStateMachineConfig returns FSM defaults func DefaultStateMachineConfig() StateMachineConfig { return StateMachineConfig{ - InitialTokensPerBlock: DefaultInitialTokensPerBlock, - BlocksPerHalvening: DefaultBlocksPerHalvening, - FaucetAddress: "", + InitialTokensPerBlock: DefaultInitialTokensPerBlock, + BlocksPerHalvening: DefaultBlocksPerHalvening, + FaucetAddress: "", + IndexOrdersByAddresses: DefaultIndexOrdersByAddresses, } }