Skip to content

Commit

Permalink
vochain/indexer: lowercase hex search terms when querying
Browse files Browse the repository at this point in the history
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 vocdoni#1348.
  • Loading branch information
mvdan committed Jul 6, 2024
1 parent 271b3ce commit 73570d6
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 14 deletions.
35 changes: 25 additions & 10 deletions vochain/indexer/indexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
stdlog "log"
"math/big"
"path/filepath"
"strings"
"testing"

qt "github.com/frankban/quicktest"
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 4 additions & 3 deletions vochain/indexer/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
"strings"
"time"

"go.vocdoni.io/proto/build/go/models"
Expand Down Expand Up @@ -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,
Expand All @@ -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),
})
Expand Down
3 changes: 2 additions & 1 deletion vochain/indexer/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"math/big"
"strings"
"time"

"go.vocdoni.io/proto/build/go/models"
Expand Down Expand Up @@ -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),
})
Expand Down

0 comments on commit 73570d6

Please sign in to comment.