Skip to content

Commit 8b00110

Browse files
committed
add partial handling of event signature collision
1 parent a3f07b2 commit 8b00110

File tree

4 files changed

+169
-171
lines changed

4 files changed

+169
-171
lines changed

seth/client.go

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"math/big"
88
"net/http"
99
"path/filepath"
10+
"reflect"
1011
"strings"
1112
"time"
1213

@@ -1167,28 +1168,56 @@ func (t TransactionLog) GetData() []byte {
11671168
}
11681169

11691170
func (m *Client) decodeContractLogs(l zerolog.Logger, logs []types.Log, allABIs []*abi.ABI) ([]DecodedTransactionLog, error) {
1170-
l.Trace().Msg("Decoding events")
1171+
l.Trace().
1172+
Msg("Decoding events")
11711173
sigMap := buildEventSignatureMap(allABIs)
11721174

11731175
var eventsParsed []DecodedTransactionLog
11741176
for _, lo := range logs {
11751177
if len(lo.Topics) == 0 {
1176-
l.Debug().Msg("Log has no topics; skipping")
1178+
l.Debug().
1179+
Msg("Log has no topics; skipping")
11771180
continue
11781181
}
11791182

11801183
eventSig := lo.Topics[0].Hex()
11811184
possibleEvents, exists := sigMap[eventSig]
11821185
if !exists {
1183-
l.Trace().Str("Signature", eventSig).Msg("No matching events found for signature")
1186+
l.Trace().
1187+
Str("Event signature", eventSig).
1188+
Msg("No matching events found for signature")
11841189
continue
11851190
}
11861191

1192+
// Check if we know what contract is this log from and if we do, get its ABI to skip unnecessary iterations
1193+
var knownContractABI *abi.ABI
1194+
if contractName := m.ContractAddressToNameMap.GetContractName(lo.Address.Hex()); contractName != "" {
1195+
maybeABI, ok := m.ContractStore.GetABI(contractName)
1196+
if !ok {
1197+
l.Trace().
1198+
Str("Event signature", eventSig).
1199+
Str("Contract name", contractName).
1200+
Str("Contract address", lo.Address.Hex()).
1201+
Msg("No ABI found for known contract; this is unexpected. Continuing with step-by-step ABI search")
1202+
} else {
1203+
knownContractABI = maybeABI
1204+
}
1205+
}
1206+
11871207
// Iterate over possible events with the same signature
11881208
matched := false
11891209
for _, evWithABI := range possibleEvents {
11901210
evSpec := evWithABI.EventSpec
1191-
eventABI := evWithABI.EventABI
1211+
contractABI := evWithABI.ContractABI
1212+
1213+
// Check if known contract ABI matches candidate ABI and if not, skip this ABI and try the next one
1214+
if knownContractABI != nil && !reflect.DeepEqual(knownContractABI, contractABI) {
1215+
l.Trace().
1216+
Str("Event signature", eventSig).
1217+
Str("Contract address", lo.Address.Hex()).
1218+
Msg("ABI doesn't match known ABI for this address; trying next ABI")
1219+
continue
1220+
}
11921221

11931222
// Validate indexed parameters count
11941223
// Non-indexed parameters are stored in the Data field,
@@ -1220,7 +1249,7 @@ func (m *Client) decodeContractLogs(l zerolog.Logger, logs []types.Log, allABIs
12201249
Str("Signature", evSpec.Sig).
12211250
Msg("Unpacking event")
12221251

1223-
eventsMap, topicsMap, err := decodeEventFromLog(l, *eventABI, *evSpec, d)
1252+
eventsMap, topicsMap, err := decodeEventFromLog(l, *contractABI, *evSpec, d)
12241253
if err != nil {
12251254
l.Error().
12261255
Err(err).
@@ -1256,55 +1285,24 @@ func (m *Client) decodeContractLogs(l zerolog.Logger, logs []types.Log, allABIs
12561285
return eventsParsed, nil
12571286
}
12581287

1259-
// func (m *Client) decodeContractLogs(l zerolog.Logger, logs []types.Log, allABIs []*abi.ABI) ([]DecodedTransactionLog, error) {
1260-
// l.Trace().Msg("Decoding events")
1261-
// var eventsParsed []DecodedTransactionLog
1262-
// for _, lo := range logs {
1263-
// ABI_LOOP:
1264-
// for _, a := range allABIs {
1265-
// for _, evSpec := range a.Events {
1266-
// if evSpec.ID.Hex() == lo.Topics[0].Hex() {
1267-
// d := TransactionLog{lo.Topics, lo.Data}
1268-
// l.Trace().Str("Name", evSpec.RawName).Str("Signature", evSpec.Sig).Msg("Unpacking event")
1269-
// eventsMap, topicsMap, err := decodeEventFromLog(l, *a, evSpec, d)
1270-
// if err != nil {
1271-
// return nil, errors.Wrap(err, ErrDecodeLog)
1272-
// }
1273-
// parsedEvent := decodedLogFromMaps(&DecodedTransactionLog{}, eventsMap, topicsMap)
1274-
// decodedTransactionLog, ok := parsedEvent.(*DecodedTransactionLog)
1275-
// if ok {
1276-
// decodedTransactionLog.Signature = evSpec.Sig
1277-
// m.mergeLogMeta(decodedTransactionLog, lo)
1278-
// eventsParsed = append(eventsParsed, *decodedTransactionLog)
1279-
// l.Trace().Interface("Log", parsedEvent).Msg("Transaction log")
1280-
// break ABI_LOOP
1281-
// }
1282-
// l.Trace().
1283-
// Str("Actual type", fmt.Sprintf("%T", decodedTransactionLog)).
1284-
// Msg("Failed to cast decoded event to DecodedCommonLog")
1285-
// }
1286-
// }
1287-
// }
1288-
// }
1289-
// return eventsParsed, nil
1290-
// }
1291-
1292-
type EventWithABI struct {
1293-
EventABI *abi.ABI
1294-
EventSpec *abi.Event
1288+
type eventWithABI struct {
1289+
ContractABI *abi.ABI
1290+
EventSpec *abi.Event
12951291
}
12961292

12971293
// buildEventSignatureMap precomputes a mapping from event signature to events with their ABIs
1298-
func buildEventSignatureMap(allABIs []*abi.ABI) map[string][]*EventWithABI {
1299-
sigMap := make(map[string][]*EventWithABI)
1294+
func buildEventSignatureMap(allABIs []*abi.ABI) map[string][]*eventWithABI {
1295+
sigMap := make(map[string][]*eventWithABI)
13001296
for _, a := range allABIs {
13011297
for _, ev := range a.Events {
1302-
sigMap[ev.ID.Hex()] = append(sigMap[ev.ID.Hex()], &EventWithABI{
1303-
EventABI: a,
1304-
EventSpec: &ev,
1298+
event := ev
1299+
sigMap[ev.ID.Hex()] = append(sigMap[ev.ID.Hex()], &eventWithABI{
1300+
ContractABI: a,
1301+
EventSpec: &event,
13051302
})
13061303
}
13071304
}
1305+
13081306
return sigMap
13091307
}
13101308

seth/client_decode_test.go

Lines changed: 120 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -75,85 +75,85 @@ func TestSmokeDebugData(t *testing.T) {
7575
}
7676

7777
tests := []test{
78-
// {
79-
// name: "test named inputs/outputs",
80-
// method: "emitNamedInputsOutputs",
81-
// params: []interface{}{big.NewInt(1337), "test"},
82-
// write: true,
83-
// output: seth.DecodedTransaction{
84-
// CommonData: seth.CommonData{
85-
// Input: map[string]interface{}{
86-
// "inputVal1": big.NewInt(1337),
87-
// "inputVal2": "test",
88-
// },
89-
// },
90-
// },
91-
// },
92-
// // TODO: https://docs.soliditylang.org/en/v0.8.19/control-structures.html read and figure out if
93-
// // decoding anynymous + named is heavily used and needed, usually people name params and omit output names
94-
// {
95-
// name: "test anonymous inputs/outputs",
96-
// method: "emitInputsOutputs",
97-
// params: []interface{}{big.NewInt(1337), "test"},
98-
// write: true,
99-
// output: seth.DecodedTransaction{
100-
// CommonData: seth.CommonData{
101-
// Input: map[string]interface{}{
102-
// "inputVal1": big.NewInt(1337),
103-
// "inputVal2": "test",
104-
// },
105-
// },
106-
// },
107-
// },
108-
// {
109-
// name: "test one log no index",
110-
// method: "emitNoIndexEvent",
111-
// write: true,
112-
// output: seth.DecodedTransaction{
113-
// Events: []seth.DecodedTransactionLog{
114-
// {
115-
// DecodedCommonLog: seth.DecodedCommonLog{
116-
// EventData: map[string]interface{}{
117-
// "sender": c.Addresses[0],
118-
// },
119-
// },
120-
// },
121-
// },
122-
// },
123-
// },
124-
// {
125-
// name: "test one log index",
126-
// method: "emitOneIndexEvent",
127-
// write: true,
128-
// output: seth.DecodedTransaction{
129-
// Events: []seth.DecodedTransactionLog{
130-
// {
131-
// DecodedCommonLog: seth.DecodedCommonLog{
132-
// EventData: map[string]interface{}{
133-
// "a": big.NewInt(83),
134-
// },
135-
// },
136-
// },
137-
// },
138-
// },
139-
// },
140-
// {
141-
// name: "test two log index",
142-
// method: "emitTwoIndexEvent",
143-
// write: true,
144-
// output: seth.DecodedTransaction{
145-
// Events: []seth.DecodedTransactionLog{
146-
// {
147-
// DecodedCommonLog: seth.DecodedCommonLog{
148-
// EventData: map[string]interface{}{
149-
// "roundId": big.NewInt(1),
150-
// "startedBy": c.Addresses[0],
151-
// },
152-
// },
153-
// },
154-
// },
155-
// },
156-
// },
78+
{
79+
name: "test named inputs/outputs",
80+
method: "emitNamedInputsOutputs",
81+
params: []interface{}{big.NewInt(1337), "test"},
82+
write: true,
83+
output: seth.DecodedTransaction{
84+
CommonData: seth.CommonData{
85+
Input: map[string]interface{}{
86+
"inputVal1": big.NewInt(1337),
87+
"inputVal2": "test",
88+
},
89+
},
90+
},
91+
},
92+
// TODO: https://docs.soliditylang.org/en/v0.8.19/control-structures.html read and figure out if
93+
// decoding anynymous + named is heavily used and needed, usually people name params and omit output names
94+
{
95+
name: "test anonymous inputs/outputs",
96+
method: "emitInputsOutputs",
97+
params: []interface{}{big.NewInt(1337), "test"},
98+
write: true,
99+
output: seth.DecodedTransaction{
100+
CommonData: seth.CommonData{
101+
Input: map[string]interface{}{
102+
"inputVal1": big.NewInt(1337),
103+
"inputVal2": "test",
104+
},
105+
},
106+
},
107+
},
108+
{
109+
name: "test one log no index",
110+
method: "emitNoIndexEvent",
111+
write: true,
112+
output: seth.DecodedTransaction{
113+
Events: []seth.DecodedTransactionLog{
114+
{
115+
DecodedCommonLog: seth.DecodedCommonLog{
116+
EventData: map[string]interface{}{
117+
"sender": c.Addresses[0],
118+
},
119+
},
120+
},
121+
},
122+
},
123+
},
124+
{
125+
name: "test one log index",
126+
method: "emitOneIndexEvent",
127+
write: true,
128+
output: seth.DecodedTransaction{
129+
Events: []seth.DecodedTransactionLog{
130+
{
131+
DecodedCommonLog: seth.DecodedCommonLog{
132+
EventData: map[string]interface{}{
133+
"a": big.NewInt(83),
134+
},
135+
},
136+
},
137+
},
138+
},
139+
},
140+
{
141+
name: "test two log index",
142+
method: "emitTwoIndexEvent",
143+
write: true,
144+
output: seth.DecodedTransaction{
145+
Events: []seth.DecodedTransactionLog{
146+
{
147+
DecodedCommonLog: seth.DecodedCommonLog{
148+
EventData: map[string]interface{}{
149+
"roundId": big.NewInt(1),
150+
"startedBy": c.Addresses[0],
151+
},
152+
},
153+
},
154+
},
155+
},
156+
},
157157
{
158158
name: "test three log index",
159159
method: "emitThreeIndexEvent",
@@ -172,47 +172,47 @@ func TestSmokeDebugData(t *testing.T) {
172172
},
173173
},
174174
},
175-
// {
176-
// name: "test log no index string",
177-
// method: "emitNoIndexEventString",
178-
// write: true,
179-
// output: seth.DecodedTransaction{
180-
// Events: []seth.DecodedTransactionLog{
181-
// {
182-
// DecodedCommonLog: seth.DecodedCommonLog{
183-
// EventData: map[string]interface{}{
184-
// "str": "myString",
185-
// },
186-
// },
187-
// },
188-
// },
189-
// },
190-
// },
191-
// // emitNoIndexStructEvent
192-
// {
193-
// name: "test log struct",
194-
// method: "emitNoIndexStructEvent",
195-
// write: true,
196-
// output: seth.DecodedTransaction{
197-
// Events: []seth.DecodedTransactionLog{
198-
// {
199-
// DecodedCommonLog: seth.DecodedCommonLog{
200-
// EventData: map[string]interface{}{
201-
// "a": struct {
202-
// Name string `json:"name"`
203-
// Balance uint64 `json:"balance"`
204-
// DailyLimit *big.Int `json:"dailyLimit"`
205-
// }{
206-
// Name: "John",
207-
// Balance: 5,
208-
// DailyLimit: big.NewInt(10),
209-
// },
210-
// },
211-
// },
212-
// },
213-
// },
214-
// },
215-
// },
175+
{
176+
name: "test log no index string",
177+
method: "emitNoIndexEventString",
178+
write: true,
179+
output: seth.DecodedTransaction{
180+
Events: []seth.DecodedTransactionLog{
181+
{
182+
DecodedCommonLog: seth.DecodedCommonLog{
183+
EventData: map[string]interface{}{
184+
"str": "myString",
185+
},
186+
},
187+
},
188+
},
189+
},
190+
},
191+
// emitNoIndexStructEvent
192+
{
193+
name: "test log struct",
194+
method: "emitNoIndexStructEvent",
195+
write: true,
196+
output: seth.DecodedTransaction{
197+
Events: []seth.DecodedTransactionLog{
198+
{
199+
DecodedCommonLog: seth.DecodedCommonLog{
200+
EventData: map[string]interface{}{
201+
"a": struct {
202+
Name string `json:"name"`
203+
Balance uint64 `json:"balance"`
204+
DailyLimit *big.Int `json:"dailyLimit"`
205+
}{
206+
Name: "John",
207+
Balance: 5,
208+
DailyLimit: big.NewInt(10),
209+
},
210+
},
211+
},
212+
},
213+
},
214+
},
215+
},
216216
// TODO: another case - figure out if indexed strings are used by anyone in events
217217
// https://ethereum.stackexchange.com/questions/6840/indexed-event-with-string-not-getting-logged
218218
}

0 commit comments

Comments
 (0)