Skip to content

Commit a6f5934

Browse files
committed
sentinel init
1 parent 989c0a5 commit a6f5934

File tree

16 files changed

+2346
-0
lines changed

16 files changed

+2346
-0
lines changed

sentinel/README.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Subscription Manager
2+
3+
## Overview
4+
5+
The **Subscription Manager** is a core component of the [Sentinel](https://github.com/smartcontractkit/sentinel) designed to facilitate efficient and organized management of blockchain event subscriptions. It enables seamless subscription and unsubscription to specific blockchain events, broadcasting of event logs to subscribed listeners, and ensures cache consistency through intelligent invalidation mechanisms.
6+
7+
> **Note:** The Subscription Manager is **not** intended to be used as a standalone component. It is tightly integrated within Sentinel to provide subscription tracking for testing applications.
8+
9+
## Features
10+
11+
- **Subscribe to Events:** Allows subscribing to specific addresses and topics, enabling listeners to receive relevant event logs.
12+
- **Unsubscribe from Events:** Facilitates the removal of subscriptions, ensuring that listeners no longer receive event logs they are no longer interested in.
13+
- **Broadcast Event Logs:** Efficiently broadcasts received event logs to all relevant subscribers, ensuring real-time updates.
14+
- **Cache Management:** Maintains an internal cache of active subscriptions and intelligently invalidates the cache upon any subscription changes to ensure data consistency.
15+
- **Thread-Safe Operations:** Utilizes mutexes to ensure that all subscription operations are thread-safe, preventing race conditions in concurrent environments.
16+
- **Comprehensive Logging:** Integrates with a logging system to provide detailed insights into subscription changes, cache invalidations, and broadcasting activities.
17+
18+
## Integration within the Chainlink Testing Framework
19+
20+
The Subscription Manager is a pivotal component within the Sentinel module of the Chainlink Testing Framework. It works in tandem with other modules to simulate and monitor blockchain interactions, making it invaluable for testing decentralized applications.
21+
22+
### How It Works
23+
24+
1. **Subscription Registration:**
25+
- **Subscribe:** Listeners can subscribe to specific events by providing the contract address and event topic. The Subscription Manager registers these subscriptions and updates its internal cache accordingly.
26+
- **Unsubscribe:** Subscribers can be removed from specific event subscriptions, prompting the Subscription Manager to update its registry and invalidate caches as necessary.
27+
28+
2. **Event Broadcasting:**
29+
- When an event log is captured, the Subscription Manager identifies all subscribers interested in that event based on the address and topic.
30+
- It then broadcasts the log to all relevant subscribers, ensuring they receive timely updates.
31+
32+
3. **Cache Invalidation:**
33+
- Any change in subscriptions (addition or removal) triggers a cache invalidation to maintain consistency.
34+
- The Subscription Manager ensures that the cache accurately reflects the current state of active subscriptions.
35+
36+
### Usage within Tests
37+
38+
While the Subscription Manager isn't meant to be used directly in isolation, it plays a crucial role in facilitating tests that require monitoring and reacting to specific blockchain events.
39+
40+
```go
41+
package subscription_manager_test
42+
43+
import (
44+
"testing"
45+
46+
"github.com/ethereum/go-ethereum/common"
47+
"github.com/stretchr/testify/assert"
48+
"github.com/stretchr/testify/require"
49+
"github.com/smartcontractkit/chainlink-testing-framework/sentinel/subscription_manager"
50+
)
51+
52+
func TestExample(t *testing.T) {
53+
manager := subscription_manager.NewSubscriptionManager(/* parameters */)
54+
55+
address := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
56+
topic := common.HexToHash("0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd")
57+
58+
// Subscribe to an event
59+
ch, err := manager.Subscribe(address, topic)
60+
require.NoError(t, err)
61+
assert.NotNil(t, ch)
62+
63+
// Perform actions that trigger the event...
64+
65+
// Listen for the event log
66+
log := <-ch
67+
assert.Equal(t, expectedLog, log)
68+
69+
// Unsubscribe when done
70+
err = manager.Unsubscribe(address, topic, ch)
71+
require.NoError(t, err)
72+
}
73+
```
74+
75+
## API Reference
76+
77+
### `Subscribe(address common.Address, topic common.Hash) (chan Log, error)`
78+
79+
**Description:**
80+
Subscribes to a specific event based on the provided contract address and event topic.
81+
82+
**Parameters:**
83+
- `address` (`common.Address`): The address of the contract emitting the event.
84+
- `topic` (`common.Hash`): The specific event topic to subscribe to.
85+
86+
**Returns:**
87+
- `chan Log`: A channel through which event logs will be received.
88+
- `error`: An error object if the subscription fails.
89+
90+
### `Unsubscribe(address common.Address, topic common.Hash, ch chan Log) error`
91+
92+
**Description:**
93+
Unsubscribes from a specific event, removing the provided channel from the subscription list.
94+
95+
**Parameters:**
96+
- `address` (`common.Address`): The address of the contract emitting the event.
97+
- `topic` (`common.Hash`): The specific event topic to unsubscribe from.
98+
- `ch` (`chan Log`): The channel to remove from the subscription list.
99+
100+
**Returns:**
101+
- `error`: An error object if the unsubscription fails.
102+
103+
### `BroadcastLog(eventKey EventKey, log Log)`
104+
105+
**Description:**
106+
Broadcasts an event log to all subscribers associated with the given event key.
107+
108+
**Parameters:**
109+
- `eventKey` (`EventKey`): A struct containing the address and topic.
110+
- `log` (`Log`): The event log to broadcast.
111+
112+
### `GetAddressesAndTopics() map[common.Address][]common.Hash`
113+
114+
**Description:**
115+
Retrieves the current mapping of subscribed addresses and their corresponding topics.
116+
117+
**Returns:**
118+
- `map[common.Address][]common.Hash`: A map where each key is an address and the value is a slice of topics subscribed to that address.
119+
120+
### `Close()`
121+
122+
**Description:**
123+
Closes the Subscription Manager, unsubscribing all listeners and cleaning up resources.
124+
125+
**Usage:**
126+
```go
127+
manager.Close()
128+
```
129+
130+
## Testing
131+
132+
The Subscription Manager is rigorously tested to ensure reliability and correctness. Tests are designed to verify:
133+
134+
- **Subscription and Unsubscription:** Ensures that subscribing and unsubscribing operations correctly modify the internal registry.
135+
- **Event Broadcasting:** Validates that event logs are accurately broadcasted to all relevant subscribers.
136+
- **Cache Invalidation:** Confirms that the internal cache is properly invalidated upon any changes in subscriptions.
137+
- **Thread Safety:** Utilizes Go's race detector to ensure that concurrent operations do not lead to race conditions.
138+
139+
### Running Tests
140+
141+
To execute the tests with the race detector enabled:
142+
143+
```bash
144+
go test -race ./subscription_manager
145+
```
146+
147+
### Mocking and Logging
148+
149+
A `MockLogger` is employed to capture and verify log messages generated by the Subscription Manager. This allows tests to assert that specific actions (like subscribing or unsubscribing) produce the expected log outputs, enhancing the robustness of the test suite.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// File: chain_poller/chain_poller.go
2+
package chain_poller
3+
4+
import (
5+
"context"
6+
"errors"
7+
8+
"github.com/smartcontractkit/chainlink-testing-framework/sentinel/internal"
9+
)
10+
11+
// ChainPollerConfig holds the configuration for the ChainPoller.
12+
type ChainPollerConfig struct {
13+
BlockchainClient BlockchainClient
14+
Logger internal.Logger
15+
ChainID int64
16+
}
17+
18+
// ChainPoller is responsible for polling logs from the blockchain.
19+
type ChainPoller struct {
20+
Config ChainPollerConfig
21+
}
22+
23+
// NewChainPoller initializes a new ChainPoller.
24+
func NewChainPoller(cfg ChainPollerConfig) (*ChainPoller, error) {
25+
if cfg.BlockchainClient == nil {
26+
return nil, errors.New("blockchain client cannot be nil")
27+
}
28+
if cfg.Logger == nil {
29+
return nil, errors.New("logger cannot be nil")
30+
}
31+
32+
return &ChainPoller{
33+
Config: cfg,
34+
}, nil
35+
}
36+
37+
// Poll fetches logs from the blockchain based on the provided filter queries.
38+
func (cp *ChainPoller) Poll(ctx context.Context, filterQueries []internal.FilterQuery) ([]internal.Log, error) {
39+
var allLogs []internal.Log
40+
41+
for _, query := range filterQueries {
42+
logs, err := cp.Config.BlockchainClient.FilterLogs(ctx, query)
43+
if err != nil {
44+
cp.Config.Logger.Error("Failed to filter logs", "error", err, "query", query)
45+
continue
46+
}
47+
allLogs = append(allLogs, logs...)
48+
}
49+
50+
return allLogs, nil
51+
}
52+
53+
var _ ChainPollerInterface = (*ChainPoller)(nil)

0 commit comments

Comments
 (0)