-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathevent.go
More file actions
136 lines (119 loc) · 4.49 KB
/
event.go
File metadata and controls
136 lines (119 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package main
import (
"context"
"encoding/binary"
"fmt"
"log"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
cid "github.com/ipfs/go-cid"
)
// AllocationCreatedEvent represents a parsed AllocationCreated event from the DDO contract.
type AllocationCreatedEvent struct {
Client common.Address
AllocationID uint64
Provider uint64
Data []byte // raw CID bytes
Size uint64
TermMin int64
TermMax int64
Expiration int64
DownloadURL string
}
// ABI JSON for the AllocationCreated event (from DDOTypes.sol).
const allocationCreatedABIJSON = `[{
"type": "event",
"name": "AllocationCreated",
"anonymous": false,
"inputs": [
{"name": "client", "type": "address", "indexed": true, "internalType": "address"},
{"name": "allocationId", "type": "uint64", "indexed": true, "internalType": "uint64"},
{"name": "provider", "type": "uint64", "indexed": true, "internalType": "uint64"},
{"name": "data", "type": "bytes", "indexed": false, "internalType": "bytes"},
{"name": "size", "type": "uint64", "indexed": false, "internalType": "uint64"},
{"name": "termMin", "type": "int64", "indexed": false, "internalType": "int64"},
{"name": "termMax", "type": "int64", "indexed": false, "internalType": "int64"},
{"name": "expiration", "type": "int64", "indexed": false, "internalType": "int64"},
{"name": "downloadURL", "type": "string", "indexed": false, "internalType": "string"}
]
}]`
var (
allocationCreatedABI abi.ABI
allocationCreatedTopic common.Hash
)
func init() {
parsed, err := abi.JSON(strings.NewReader(allocationCreatedABIJSON))
if err != nil {
panic("failed to parse AllocationCreated ABI: " + err.Error())
}
allocationCreatedABI = parsed
allocationCreatedTopic = crypto.Keccak256Hash(
[]byte("AllocationCreated(address,uint64,uint64,bytes,uint64,int64,int64,int64,string)"),
)
}
// bytesToCID parses raw CID bytes from the event data field.
func bytesToCID(data []byte) (cid.Cid, error) {
_, c, err := cid.CidFromBytes(data)
if err != nil {
return cid.Undef, fmt.Errorf("failed to parse CID from bytes: %w", err)
}
return c, nil
}
// parseAllocationCreatedLog extracts an AllocationCreatedEvent from a raw Ethereum log.
// Indexed fields (client, allocationId, provider) come from topics[1-3].
// Non-indexed fields (data, size, termMin, termMax, expiration, downloadURL) are ABI-decoded from log.Data.
func parseAllocationCreatedLog(vLog types.Log) (*AllocationCreatedEvent, error) {
if len(vLog.Topics) < 4 {
return nil, fmt.Errorf("expected 4 topics, got %d", len(vLog.Topics))
}
evt := &AllocationCreatedEvent{}
// Indexed fields from topics (each topic is 32 bytes, values are right-aligned)
evt.Client = common.BytesToAddress(vLog.Topics[1].Bytes())
evt.AllocationID = binary.BigEndian.Uint64(vLog.Topics[2].Bytes()[24:])
evt.Provider = binary.BigEndian.Uint64(vLog.Topics[3].Bytes()[24:])
// Non-indexed fields via ABI unpack
nonIndexed := allocationCreatedABI.Events["AllocationCreated"].Inputs.NonIndexed()
values, err := nonIndexed.UnpackValues(vLog.Data)
if err != nil {
return nil, fmt.Errorf("failed to unpack event data: %w", err)
}
if len(values) < 6 {
return nil, fmt.Errorf("expected 6 non-indexed values, got %d", len(values))
}
evt.Data = values[0].([]byte)
evt.Size = values[1].(uint64)
evt.TermMin = values[2].(int64)
evt.TermMax = values[3].(int64)
evt.Expiration = values[4].(int64)
evt.DownloadURL = values[5].(string)
return evt, nil
}
// fetchAllocationEvents queries the Ethereum client for AllocationCreated logs within a block range.
func fetchAllocationEvents(ctx context.Context, client *ethclient.Client, contractAddr common.Address, fromBlock, toBlock *big.Int) ([]AllocationCreatedEvent, error) {
query := ethereum.FilterQuery{
FromBlock: fromBlock,
ToBlock: toBlock,
Addresses: []common.Address{contractAddr},
Topics: [][]common.Hash{{allocationCreatedTopic}},
}
logs, err := client.FilterLogs(ctx, query)
if err != nil {
return nil, fmt.Errorf("failed to filter logs: %w", err)
}
var events []AllocationCreatedEvent
for _, l := range logs {
evt, err := parseAllocationCreatedLog(l)
if err != nil {
log.Printf("WARN: skipping log in block %d tx %s: %v", l.BlockNumber, l.TxHash.Hex(), err)
continue
}
events = append(events, *evt)
}
return events, nil
}