From 73570d6c1ed58f5babfb29791351f64cbf250ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Fri, 5 Jul 2024 15:51:08 +0100 Subject: [PATCH] vochain/indexer: lowercase hex search terms when querying Since the SQL query uses INSTR with LOWER and HEX, the input hex string will never match if it contains any uppercase characters. Normalize the input string as lowercase to avoid that issue. In particular, uppercase letters can happen due to https://eips.ethereum.org/EIPS/eip-55. Fixes #1348. --- vochain/indexer/indexer_test.go | 35 +++++++++++++++++++++++---------- vochain/indexer/process.go | 7 ++++--- vochain/indexer/vote.go | 3 ++- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/vochain/indexer/indexer_test.go b/vochain/indexer/indexer_test.go index ce0b8c9c7..e5be9e659 100644 --- a/vochain/indexer/indexer_test.go +++ b/vochain/indexer/indexer_test.go @@ -9,6 +9,7 @@ import ( stdlog "log" "math/big" "path/filepath" + "strings" "testing" qt "github.com/frankban/quicktest" @@ -255,20 +256,19 @@ func TestEntitySearch(t *testing.T) { app.AdvanceTestBlock() // Exact entity search list := idx.EntityList(10, 0, "4011d50537fa164b6fef261141797bbe4014526e") - if len(list) < 1 { - t.Fatalf("expected 1 entity, got %d", len(list)) - } + qt.Assert(t, list, qt.HasLen, 1) // Search for nonexistent entity list = idx.EntityList(10, 0, "4011d50537fa164b6fef261141797bbe4014526f") - if len(list) > 0 { - t.Fatalf("expected 0 entities, got %d", len(list)) - } + qt.Assert(t, list, qt.HasLen, 0) // Search containing part of all manually-defined entities list = idx.EntityList(10, 0, "011d50537fa164b6fef261141797bbe4014526e") - log.Info(list) - if len(list) < len(entityIds) { - t.Fatalf("expected %d entities, got %d", len(entityIds), len(list)) - } + qt.Assert(t, list, qt.HasLen, len(entityIds)) + // Partial entity search as mixed case hex + list = idx.EntityList(10, 0, "50537FA164B6Fef261141797BbE401452") + qt.Assert(t, list, qt.HasLen, len(entityIds)) + // Partial entity search as uppercase hex + list = idx.EntityList(10, 0, "50537FA164B6FEF261141797BBE401452") + qt.Assert(t, list, qt.HasLen, len(entityIds)) } func TestProcessList(t *testing.T) { @@ -487,6 +487,15 @@ func TestProcessSearch(t *testing.T) { t.Fatalf("expected %d processes, got %d", len(endedPIDs), len(list)) } + // Partial process search as uppercase hex + list, err = idx.ProcessList(eidTest, 0, 10, "011D50537FA164B6FEF261141797BBE4014526E", 0, 0, "", false) + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, list, qt.HasLen, len(processIds)) + // Partial process search as mixed case hex + list, err = idx.ProcessList(eidTest, 0, 10, "011D50537fA164B6FeF261141797BbE4014526E", 0, 0, "", false) + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, list, qt.HasLen, len(processIds)) + // Search with an exact Entity ID, but starting with a null byte. // This can trip up sqlite, as it assumes TEXT strings are NUL-terminated. list, err = idx.ProcessList([]byte("\x00foobar"), 0, 100, "", 0, 0, "", false) @@ -707,6 +716,12 @@ func TestResults(t *testing.T) { qt.Assert(t, envelopes, qt.HasLen, 1) qt.Assert(t, envelopes[0].Height, qt.Equals, matchHeight) + // Partial vote search as uppercase hex + envelopes, err = idx.GetEnvelopes(pid, 10, 0, strings.ToUpper(matchNullifier[:29])) + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, envelopes, qt.HasLen, 1) + qt.Assert(t, envelopes[0].Height, qt.Equals, matchHeight) + // Test results proc, err := idx.ProcessInfo(pid) qt.Assert(t, err, qt.IsNil) diff --git a/vochain/indexer/process.go b/vochain/indexer/process.go index 3baeb08f4..3a103cc3b 100644 --- a/vochain/indexer/process.go +++ b/vochain/indexer/process.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "strings" "time" "go.vocdoni.io/proto/build/go/models" @@ -72,7 +73,7 @@ func (idx *Indexer) ProcessList(entityID []byte, from, max int, searchTerm strin Namespace: int64(namespace), Status: int64(statusnum), SourceNetworkID: int64(srcNetworkId), - IDSubstr: searchTerm, + IDSubstr: strings.ToLower(searchTerm), // we search in lowercase Offset: int64(from), Limit: int64(max), WithResults: withResults, @@ -95,10 +96,10 @@ func (idx *Indexer) CountTotalProcesses() uint64 { // EntityList returns the list of entities indexed by the indexer // searchTerm is optional, if declared as zero-value -// will be ignored. Searches against the ID field. +// will be ignored. Searches against the ID field as lowercase hex. func (idx *Indexer) EntityList(max, from int, searchTerm string) []indexerdb.SearchEntitiesRow { rows, err := idx.readOnlyQuery.SearchEntities(context.TODO(), indexerdb.SearchEntitiesParams{ - EntityIDSubstr: searchTerm, + EntityIDSubstr: strings.ToLower(searchTerm), // we search in lowercase Offset: int64(from), Limit: int64(max), }) diff --git a/vochain/indexer/vote.go b/vochain/indexer/vote.go index 9aaa09297..d2c2edcbc 100644 --- a/vochain/indexer/vote.go +++ b/vochain/indexer/vote.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "math/big" + "strings" "time" "go.vocdoni.io/proto/build/go/models" @@ -72,7 +73,7 @@ func (idx *Indexer) GetEnvelopes(processId []byte, max, from int, envelopes := []*indexertypes.EnvelopeMetadata{} txRefs, err := idx.readOnlyQuery.SearchVotes(context.TODO(), indexerdb.SearchVotesParams{ ProcessID: processId, - NullifierSubstr: searchTerm, + NullifierSubstr: strings.ToLower(searchTerm), // we search in lowercase Limit: int64(max), Offset: int64(from), })