Skip to content

Commit 7ccdd65

Browse files
committed
Add README and a warning in case multiple plugins attempt to add token data for the same message.
1 parent 9f8c649 commit 7ccdd65

File tree

3 files changed

+65
-6
lines changed

3 files changed

+65
-6
lines changed

execute/tokendata/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Token Data Observer
2+
3+
This package implements an observer for fetching token data from offchain locations. The primary use case for this data is 3rd party attestation services (CCTP/USDC, LBTC), but in theory it could be used to fetch any token data for unknown use cases.
4+
5+
The observer provides two primary functions:
6+
* Observe(ctx, messages) (observations, error)
7+
* IsTokenSupported(sourceSelector, msgTokenTransfer) bool
8+
9+
The OCR plugin simply calls **Observe** and includes token data in the final report as necessary.
10+
11+
# Architecture
12+
13+
The **TokenDataObserver** is built by composing many parts which all implement the same two interfaces:
14+
* **TokenDataObserver**
15+
* **AttestionClient**
16+
17+
We'll go into detail on each of these components, but first, here is a diagram which shows how everything is assembled for the **Execute** plugin:
18+
```mermaid
19+
graph TD
20+
subgraph A[BackgroundTokenDataObserver]
21+
direction LR
22+
subgraph B[CompositeTokenDataObserver]
23+
subgraph C[USDCTokenDataObserver]
24+
subgraph C1[CompositeUSDCTokenDataReader]
25+
C1a[EVMUSDC TokenDataReader]
26+
C1b[SolanaUSDC TokenDataReader]
27+
end
28+
C2["Observed [USDCAttestationClient]"]
29+
end
30+
subgraph D[LBTCTokenDataObserver]
31+
D1[LBTC TokenDataReader]
32+
D2["Observed [LBTCAttestationClient]"]
33+
end
34+
end
35+
end
36+
```
37+
38+
## Composite Token Data Observer
39+
40+
In order to support multiple token types, a special **compositeTokenDataObserver** exists to dispatch calls to token specific Observers. At a high level it holds a slice of **TokenDataObserver** objects and calls **Observe** on each of them. Results from all observers are merged together to return a single response.
41+
42+
Configuration for which tokens are supported is done using data stored in the **Home Chain**. See **NewConfigBasedCompositeObservers** for the logic which initializes token specific observers. The actual configuration if found at **pluginconfig/token.go** in the **TokenDataObserverConfig** struct.
43+
44+
## Token Specific Observers
45+
46+
Token specific observers (USDC, LBTC) are initialized as part of the **compositeTokenDataObserver**. They implement the same **TokenDataObserver** as everything else but have token specific logic.
47+
48+
To add a new token, you would create a new package in this directory and implement the interface.
49+
50+
## Attestation Client & Metrics
51+
52+
The **AttestationClient** interface is a small wrapper for an http client. It is only used by the token specific observers and should have a token specific implementation. The main purpose of this interface is to be wrapped by an **ObservedAttestationClient**, which logs prometheus metrics.
53+
54+
## Background Processing & Caching
55+
56+
Data fetching happens as a background task. This is done to avoid latency introduced by calling 3rd party services. The **backgroundObserver** object implements the same **TokenDataObserver** and wraps the **compositeTokenDataObserver**. Instead of calling the real **Observe** function immediately, it manages a cache and only returns cached data. Any messages which had not been cached previously are sent to a queue where a background task will call the **Observe** function and add results to the cache. In future rounds, the data will be cached for immediate retrieval.

execute/tokendata/observer/observer.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func (c *compositeTokenDataObserver) Observe(
135135
c.lggr.Error("Error while observing token data", "error", err)
136136
continue
137137
}
138-
merged, err = merge(tokenDataObservations, tokenData)
138+
merged, err = merge(c.lggr, tokenDataObservations, tokenData)
139139
if err != nil {
140140
c.lggr.Error("Error while merging token data",
141141
"error", err)
@@ -199,6 +199,7 @@ func (c *compositeTokenDataObserver) initTokenDataObservations(
199199
// merge merges token data from two observations, it's used to combine token data from multiple observers.
200200
// In case of token data mismatch, it returns an error and the base observation.
201201
func merge(
202+
lggr logger.Logger,
202203
base exectypes.TokenDataObservations,
203204
from exectypes.TokenDataObservations,
204205
) (exectypes.TokenDataObservations, error) {
@@ -211,7 +212,9 @@ func merge(
211212
// Merge only TokenData created by the observer
212213
for i, newTokenData := range messageTokenData.TokenData {
213214
if base[chainSelector][seq].TokenData[i].IsReady() {
214-
// Already processed by another observer, skip or raise a warning
215+
lggr.Warnw("Token data already processed by another observer",
216+
"chainSelector", chainSelector,
217+
"seqNum", seq)
215218
continue
216219
}
217220
if newTokenData.Supported {

pkg/reader/usdc_reader.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,19 +121,19 @@ func NewUSDCMessageReader(
121121
}
122122
}
123123

124-
return compositeFamilyUSDCMessageReader{
124+
return compositeUSDCMessageReader{
125125
lggr: lggr,
126126
readers: readers,
127127
}, nil
128128
}
129129

130-
// compositeFamilyUSDCMessageReader is a USDCMessageReader that can handle different chain families.
131-
type compositeFamilyUSDCMessageReader struct {
130+
// compositeUSDCMessageReader is a USDCMessageReader that can handle different chain families.
131+
type compositeUSDCMessageReader struct {
132132
lggr logger.Logger
133133
readers map[cciptypes.ChainSelector]USDCMessageReader
134134
}
135135

136-
func (m compositeFamilyUSDCMessageReader) MessagesByTokenID(
136+
func (m compositeUSDCMessageReader) MessagesByTokenID(
137137
ctx context.Context,
138138
source, dest cciptypes.ChainSelector,
139139
tokens map[MessageTokenID]cciptypes.RampTokenAmount,

0 commit comments

Comments
 (0)