From c68feb4f3db8179a2ded65b9ad5b3401e7d19fb5 Mon Sep 17 00:00:00 2001 From: ylembachar Date: Fri, 23 Jan 2026 19:27:59 +0100 Subject: [PATCH 1/2] fix: derive index topic offset from abi order --- internal/usecase/eventtrigger.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/usecase/eventtrigger.go b/internal/usecase/eventtrigger.go index 15ed154..917f4f9 100644 --- a/internal/usecase/eventtrigger.go +++ b/internal/usecase/eventtrigger.go @@ -127,6 +127,19 @@ func Topic0(sig sigparser.Signature) shs.LogPredicate { return lp } +// indexedOffsetsForInputs returns topic offsets for indexed inputs in ABI order. +func indexedOffsetsForInputs(inputs []sigparser.Parameter) []uint64 { + indexedOffsets := make([]uint64, len(inputs)) + indexedCount := uint64(0) + for i, input := range inputs { + if input.Indexed { + indexedOffsets[i] = 1 + indexedCount + indexedCount++ + } + } + return indexedOffsets +} + func logPredicates(args []EventArgument, evtSig string) ([]shs.LogPredicate, error) { lps := []shs.LogPredicate{} sig, err := sigparser.ParseSignature(evtSig) @@ -135,7 +148,7 @@ func logPredicates(args []EventArgument, evtSig string) ([]shs.LogPredicate, err } lp := Topic0(sig) lps = append(lps, lp) - indexedOffset := uint64(1) + indexedOffsets := indexedOffsetsForInputs(sig.Inputs) nonIndexedOffset := uint64(4) length := uint64(0) argnames := make([]string, len(args)) @@ -158,7 +171,7 @@ func logPredicates(args []EventArgument, evtSig string) ([]shs.LogPredicate, err } argnames[i] = arg.Name } - for _, input := range sig.Inputs { + for inputIndex, input := range sig.Inputs { lp := shs.LogPredicate{} i := slices.IndexFunc( args, @@ -180,8 +193,7 @@ func logPredicates(args []EventArgument, evtSig string) ([]shs.LogPredicate, err } lp.ValuePredicate.Op = shs.BytesEq lp.ValuePredicate.ByteArgs = [][]byte{Align(val)} - lp.LogValueRef.Offset = indexedOffset - indexedOffset++ + lp.LogValueRef.Offset = indexedOffsets[inputIndex] // input is data argument: } else { if input.Type != "uint256" { From 5cc6cb1d1ba2ec7384bc57d6e541307177660140 Mon Sep 17 00:00:00 2001 From: ylembachar Date: Fri, 23 Jan 2026 20:35:59 +0100 Subject: [PATCH 2/2] test: cover index arg offset in trigger definition --- internal/service/crypto_test.go | 119 ++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 43 deletions(-) diff --git a/internal/service/crypto_test.go b/internal/service/crypto_test.go index 11351d4..d5aa09d 100644 --- a/internal/service/crypto_test.go +++ b/internal/service/crypto_test.go @@ -9,9 +9,9 @@ import ( "strings" "testing" - sigparser "github.com/defiweb/go-sigparser" + "github.com/defiweb/go-sigparser" "github.com/ethereum/go-ethereum/common" - hexutil "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/gin-gonic/gin" shs "github.com/shutter-network/rolling-shutter/rolling-shutter/keyperimpl/shutterservice" shcommon "github.com/shutter-network/shutter-api/common" @@ -54,50 +54,83 @@ func TestEventDecryptionValidation(t *testing.T) { func TestEventDecryptionData(t *testing.T) { router := setupRouter() - bites := `{"contract": "0x4d6dd1382aa09be1d243f8960409a1ab3d913f43", "eventSig":"event Transfer(address indexed from, address indexed to, uint256 amount)","arguments": [{"name": "from", "op": "eq", "bytes": "0x9e13976721ebff885611c8391d9b02749c1283fa"},{"name": "amount", "op": "gte", "number": "1"}]}` - w := httptest.NewRecorder() - fromAsBytes, err := hexutil.Decode("0x9e13976721ebff885611c8391d9b02749c1283fa") - assert.NilError(t, err, "hex decode failed") - var req usecase.EventTriggerDefinitionRequest - err = json.NewDecoder(strings.NewReader(bites)).Decode(&req) - assert.NilError(t, err, "invalid json") - sig, err := sigparser.ParseSignature(req.EventSignature) - g := shs.EventTriggerDefinition{ - Contract: common.HexToAddress("0x4D6dD1382AA09be1d243F8960409A1ab3d913F43"), - LogPredicates: []shs.LogPredicate{ - usecase.Topic0(sig), - { - LogValueRef: shs.LogValueRef{ - Offset: 1, - Length: 1, - }, - ValuePredicate: shs.ValuePredicate{ - Op: shs.BytesEq, - ByteArgs: [][]byte{usecase.Align(fromAsBytes)}, - }, - }, - { - LogValueRef: shs.LogValueRef{ - Offset: 4, - Length: 1, + + assertTriggerDefinitionEquals := func(t *testing.T, body string, expected shs.EventTriggerDefinition) { + t.Helper() + + etd := usecase.EventTriggerDefinitionResponse{ + EventTriggerDefinition: shcommon.PrefixWith0x(hex.EncodeToString(expected.MarshalBytes())), + } + expectedJSON, err := json.Marshal(etd) + assert.NilError(t, err, "error marshalling") + + w := httptest.NewRecorder() + request, _ := http.NewRequest("POST", "/test", strings.NewReader(body)) + router.ServeHTTP(w, request) + + assert.Equal(t, 200, w.Code) + require.JSONEq(t, string(expectedJSON), w.Body.String(), "roundtrip failed") + } + + t.Run("indexed from + amount", func(t *testing.T) { + + body := `{"contract": "0x4d6dd1382aa09be1d243f8960409a1ab3d913f43", "eventSig":"event Transfer(address indexed from, address indexed to, uint256 amount)","arguments": [{"name": "from", "op": "eq", "bytes": "0x9e13976721ebff885611c8391d9b02749c1283fa"},{"name": "amount", "op": "gte", "number": "1"}]}` + fromAsBytes, err := hexutil.Decode("0x9e13976721ebff885611c8391d9b02749c1283fa") + assert.NilError(t, err, "hex decode failed") + var req usecase.EventTriggerDefinitionRequest + err = json.NewDecoder(strings.NewReader(body)).Decode(&req) + assert.NilError(t, err, "invalid json") + sig, err := sigparser.ParseSignature(req.EventSignature) + expected := shs.EventTriggerDefinition{ + Contract: common.HexToAddress("0x4D6dD1382AA09be1d243F8960409A1ab3d913F43"), + LogPredicates: []shs.LogPredicate{ + usecase.Topic0(sig), + { + LogValueRef: shs.LogValueRef{Offset: 1, Length: 1}, + ValuePredicate: shs.ValuePredicate{ + Op: shs.BytesEq, + ByteArgs: [][]byte{usecase.Align(fromAsBytes)}, + }, }, - ValuePredicate: shs.ValuePredicate{ - Op: shs.UintGte, - IntArgs: []*big.Int{big.NewInt(1)}, + { + LogValueRef: shs.LogValueRef{Offset: 4, Length: 1}, + ValuePredicate: shs.ValuePredicate{ + Op: shs.UintGte, + IntArgs: []*big.Int{big.NewInt(1)}, + }, }, }, - }, - } + } - etd := usecase.EventTriggerDefinitionResponse{ - EventTriggerDefinition: shcommon.PrefixWith0x(hex.EncodeToString(g.MarshalBytes())), - } - expected, err := json.Marshal(etd) - assert.NilError(t, err, "error marshalling") + assertTriggerDefinitionEquals(t, body, expected) + }) - request, _ := http.NewRequest("POST", "/test", strings.NewReader(bites)) - router.ServeHTTP(w, request) + t.Run("indexed to uses offset 2", func(t *testing.T) { + body := `{"contract": "0x4d6dd1382aa09be1d243f8960409a1ab3d913f43", "eventSig":"event Transfer(address indexed from, address indexed to, uint256 value)","arguments": [{"name": "to", "op": "eq", "bytes":"0x7e5f4552091a69125d5dfcb7b8c2659029395bdf"}]}` + toAsBytes, err := hexutil.Decode("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf") + assert.NilError(t, err, "hex decode failed") + var req usecase.EventTriggerDefinitionRequest + err = json.NewDecoder(strings.NewReader(body)).Decode(&req) + assert.NilError(t, err, "invalid json") + sig, err := sigparser.ParseSignature(req.EventSignature) + assert.NilError(t, err, "invalid signature") + expected := shs.EventTriggerDefinition{ + Contract: common.HexToAddress("0x4d6dd1382aa09be1d243f8960409a1ab3d913f43"), + LogPredicates: []shs.LogPredicate{ + usecase.Topic0(sig), + { + LogValueRef: shs.LogValueRef{ + Offset: 2, + Length: 1, + }, + ValuePredicate: shs.ValuePredicate{ + Op: shs.BytesEq, + ByteArgs: [][]byte{usecase.Align(toAsBytes)}, + }, + }, + }, + } - assert.Equal(t, 200, w.Code) - require.JSONEq(t, string(expected), w.Body.String(), "roundtrip failed") -} + assertTriggerDefinitionEquals(t, body, expected) + }) +} \ No newline at end of file