Sentinel
🚧 Beta Notice: Sentinel is currently in Beta mode. The API is subject to change, and users are advised to stay updated with the latest releases and documentation.
Table of Contents
Overview
Sentinel is a centralized orchestrator that manages multiple blockchain poller services, each responsible for a specific blockchain network (e.g., Ethereum, Optimism, Arbitrum). It provides a unified interface for subscribing to blockchain events, ensuring efficient log polling and event broadcasting to subscribers.
Key Features
- Multi-Chain Support: Manage multiple blockchain networks concurrently.
- Event Broadcasting: Relay blockchain events to subscribers via a thread-safe subscription system.
- Flexible Subscriptions: Dynamically subscribe and unsubscribe to events based on addresses and topics.
- Graceful Lifecycle Management: Start, stop, and clean up resources across services effortlessly.
- Comprehensive Testing: Ensures reliability through extensive unit and integration tests.
- Scalable Architecture: Designed to handle polling multiple chains with multiple users subscribed to multiple events.
System Architecture
How Components Interact
graph TD
Sentinel["Sentinel<br/>(Coordinator)"]
subgraph Ethereum
ChainPollerSvc_Ethereum["ChainPollerSvc<br/>(Ethereum)"]
ChainPoller_Ethereum["ChainPoller<br/>(Log Fetching)"]
SubscriptionManager_Ethereum["Subscription Manager"]
ChainPollerSvc_Ethereum --> ChainPoller_Ethereum
ChainPollerSvc_Ethereum --> SubscriptionManager_Ethereum
ChainPoller_Ethereum --> Blockchain_Ethereum["Blockchain<br/>(Ethereum)"]
end
subgraph Polygon
ChainPollerSvc_Polygon["ChainPollerSvc<br/>(Polygon)"]
ChainPoller_Polygon["ChainPoller<br/>(Log Fetching)"]
SubscriptionManager_Polygon["Subscription Manager"]
ChainPollerSvc_Polygon --> ChainPoller_Polygon
ChainPollerSvc_Polygon --> SubscriptionManager_Polygon
ChainPoller_Polygon --> Blockchain_Polygon["Blockchain<br/>(Polygon)"]
end
subgraph Arbitrum
ChainPollerSvc_Arbitrum["ChainPollerSvc<br/>(Arbitrum)"]
ChainPoller_Arbitrum["ChainPoller<br/>(Log Fetching)"]
SubscriptionManager_Arbitrum["Subscription Manager"]
ChainPollerSvc_Arbitrum --> ChainPoller_Arbitrum
ChainPollerSvc_Arbitrum --> SubscriptionManager_Arbitrum
ChainPoller_Arbitrum --> Blockchain_Arbitrum["Blockchain<br/>(Arbitrum)"]
end
Sentinel --> Ethereum
Sentinel --> Polygon
Sentinel --> Arbitrum
Core Components
-
Sentinel:
- Role: Central coordinator managing multiple
ChainPollerService
instances. - Visibility: External
- Responsibilities:
- Handles adding and removing blockchain chains.
- Manages global subscriptions.
- Orchestrates communication between components.
- Role: Central coordinator managing multiple
-
ChainPollerService:
- Role: Manages the polling process for a specific blockchain.
- Visibility: Internal
- Responsibilities:
- Polls blockchain logs based on filter queries.
- Integrates internal
ChainPoller
andSubscriptionManager
. - Broadcasts fetched logs to relevant subscribers.
-
ChainPoller:
- Role: Fetches logs from blockchain networks.
- Visibility: Internal
- Responsibilities:
- Interacts with the blockchain client to retrieve logs.
- Processes filter queries to fetch relevant logs.
-
SubscriptionManager:
- Role: Manages event subscriptions for a specific chain.
- Visibility: Internal
- Responsibilities:
- Tracks subscriptions to blockchain events.
- Ensures thread-safe management of subscribers.
- Broadcasts logs to all relevant subscribers.
Usage
Initialize Sentinel
Set up a Sentinel
instance:
package main
import (
"github.com/rs/zerolog"
"os"
"github.com/smartcontractkit/chainlink-testing-framework/sentinel"
)
func main() {
// Initialize logger
logger := zerolog.New(os.Stdout).With().Timestamp().Logger()
// Initialize Sentinel
sentinelCoordinator := sentinel.NewSentinel(sentinel.SentinelConfig{
Logger: &logger,
})
defer sentinelCoordinator.Close()
}
Add a Chain
Add a blockchain to monitor:
package main
import (
"time"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/smartcontractkit/chainlink-testing-framework/sentinel/blockchain_client_wrapper"
"github.com/smartcontractkit/chainlink-testing-framework/sentinel/sentinel"
)
func main() {
// Initialize logger and Sentinel as shown above
// Setup blockchain client (e.g., Geth)
client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR-PROJECT-ID")
if err != nil {
panic("Failed to connect to blockchain client: " + err.Error())
}
wrappedClient := blockchain_client_wrapper.NewGethClientWrapper(client)
// Add a new chain to Sentinel
err = sentinelCoordinator.AddChain(sentinel.AddChainConfig{
ChainID: 1, // Ethereum Mainnet
PollInterval: 10 * time.Second,
BlockchainClient: wrappedClient,
})
if err != nil {
panic("Failed to add chain: " + err.Error())
}
}
Subscribe to Events
Subscribe to blockchain events:
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/chainlink-testing-framework/sentinel/api"
)
func main() {
// Initialize logger, Sentinel, and add a chain as shown above
// Define the address and topic to subscribe to
address := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
topic := common.HexToHash("0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef")
// Subscribe to the event
logCh, err := sentinelCoordinator.Subscribe(1, address, topic)
if err != nil {
panic("Failed to subscribe: " + err.Error())
}
defer sentinelCoordinator.Unsubscribe(1, address, topic, logCh)
// Listen for logs in a separate goroutine
go func() {
for log := range logCh {
fmt.Printf("Received log: %+v\n", log)
}
}()
}
Unsubscribe
Unsubscribe from events:
package main
func main() {
// Initialize logger, Sentinel, add a chain, and subscribe as shown above
// Assume logCh is the channel obtained from Subscribe
err = sentinelCoordinator.Unsubscribe(1, address, topic, logCh)
if err != nil {
panic("Failed to unsubscribe: " + err.Error())
}
}
Remove a Chain
Remove a blockchain from monitoring:
package main
func main() {
// Initialize logger, Sentinel, add a chain, and subscribe as shown above
// Remove the chain
err = sentinelCoordinator.RemoveChain(1)
if err != nil {
panic("Failed to remove chain: " + err.Error())
}
}
API Reference
Sentinel
-
NewSentinel(config SentinelConfig) *Sentinel
Initializes a new Sentinel instance. -
AddChain(config AddChainConfig) error
Adds a new blockchain chain to Sentinel. -
RemoveChain(chainID int64) error
Removes an existing chain from Sentinel. -
Subscribe(chainID int64, address common.Address, topic common.Hash) (chan api.Log, error)
Subscribes to a specific event on a given chain. -
Unsubscribe(chainID int64, address common.Address, topic common.Hash, ch chan api.Log) error
Unsubscribes from a specific event.
Testing
Run Tests
Run the comprehensive test suite using:
go test -race ./... -v