Skip to content

Commit

Permalink
Add skeleton for lbtc attestation API integration
Browse files Browse the repository at this point in the history
  • Loading branch information
NourElRashidy authored and bukata-sa committed Nov 29, 2024
1 parent 75b1a6f commit 0be81b0
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 5 deletions.
36 changes: 32 additions & 4 deletions core/services/ocr2/plugins/ccip/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (c *DynamicPriceGetterConfig) Validate() error {
type ExecPluginJobSpecConfig struct {
SourceStartBlock, DestStartBlock uint64 // Only for first time job add.
USDCConfig USDCConfig
LBTCConfig LBTCConfig
}

type USDCConfig struct {
Expand All @@ -119,10 +120,20 @@ type USDCConfig struct {
AttestationAPIIntervalMilliseconds int
}

type LBTCConfig struct {
SourceTokenAddress common.Address
SourceMessageTransmitterAddress common.Address
AttestationAPI string
AttestationAPITimeoutSeconds uint
// AttestationAPIIntervalMilliseconds can be set to -1 to disable or 0 to use a default interval.
AttestationAPIIntervalMilliseconds int
}

type ExecPluginConfig struct {
SourceStartBlock, DestStartBlock uint64 // Only for first time job add.
IsSourceProvider bool
USDCConfig USDCConfig
LBTCConfig LBTCConfig
JobID string
}

Expand All @@ -136,16 +147,33 @@ func (e ExecPluginConfig) Encode() ([]byte, error) {

func (uc *USDCConfig) ValidateUSDCConfig() error {
if uc.AttestationAPI == "" {
return errors.New("AttestationAPI is required")
return errors.New("USDCConfig: AttestationAPI is required")
}
if uc.AttestationAPIIntervalMilliseconds < -1 {
return errors.New("AttestationAPIIntervalMilliseconds must be -1 to disable, 0 for default or greater to define the exact interval")
return errors.New("USDCConfig: AttestationAPIIntervalMilliseconds must be -1 to disable, 0 for default or greater to define the exact interval")
}
if uc.SourceTokenAddress == utils.ZeroAddress {
return errors.New("SourceTokenAddress is required")
return errors.New("USDCConfig: SourceTokenAddress is required")
}
if uc.SourceMessageTransmitterAddress == utils.ZeroAddress {
return errors.New("SourceMessageTransmitterAddress is required")
return errors.New("USDCConfig: SourceMessageTransmitterAddress is required")
}

return nil
}

func (lc *LBTCConfig) ValidateLBTCConfig() error {
if lc.AttestationAPI == "" {
return errors.New("LBTCConfig: AttestationAPI is required")
}
if lc.AttestationAPIIntervalMilliseconds < -1 {
return errors.New("LBTCConfig: AttestationAPIIntervalMilliseconds must be -1 to disable, 0 for default or greater to define the exact interval")
}
if lc.SourceTokenAddress == utils.ZeroAddress {
return errors.New("LBTCConfig: SourceTokenAddress is required")
}
if lc.SourceMessageTransmitterAddress == utils.ZeroAddress {
return errors.New("LBTCConfig: SourceMessageTransmitterAddress is required")
}

return nil
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ccipdata

// TODO: Implement lbtc token reader
type LBTCReader interface {
}
10 changes: 9 additions & 1 deletion core/services/ocr2/plugins/ccip/tokendata/http/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import (
)

type IHttpClient interface {
// Get issue a GET request to the given url and return the response body and status code.
// Get issues a GET request to the given url and returns the response body and status code.
Get(ctx context.Context, url string, timeout time.Duration) ([]byte, int, http.Header, error)

// Post issues a POST request to the given url with the given request data and returns the response body and status code.
Post(ctx context.Context, url string, requestData io.Reader, timeout time.Duration) ([]byte, int, http.Header, error)
}

type HttpClient struct {
Expand Down Expand Up @@ -46,3 +49,8 @@ func (s *HttpClient) Get(ctx context.Context, url string, timeout time.Duration)
body, err := io.ReadAll(res.Body)
return body, res.StatusCode, res.Header, err
}

func (s *HttpClient) Post(ctx context.Context, url string, requestData io.Reader, timeout time.Duration) ([]byte, int, http.Header, error) {
// TODO: Implement
return nil, 0, nil, nil
}
162 changes: 162 additions & 0 deletions core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package lbtc

import (
"context"
"errors"
"net/url"
"sync"
"time"

"github.com/ethereum/go-ethereum/common"
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/http"
"golang.org/x/time/rate"
)

// TODO: double check the validty of default values for lombard's API after checking docs
const (
apiVersion = "v1"
attestationPath = "deposits/getByHash"
defaultAttestationTimeout = 5 * time.Second

// defaultCoolDownDurationSec defines the default time to wait after getting rate limited.
// this value is only used if the 429 response does not contain the Retry-After header
defaultCoolDownDuration = 5 * time.Minute

// maxCoolDownDuration defines the maximum duration we can wait till firing the next request
maxCoolDownDuration = 10 * time.Minute

// defaultRequestInterval defines the rate in requests per second that the attestation API can be called.
// this is set according to the APIs documentated 10 requests per second rate limit.
defaultRequestInterval = 100 * time.Millisecond

// APIIntervalRateLimitDisabled is a special value to disable the rate limiting.
APIIntervalRateLimitDisabled = -1
// APIIntervalRateLimitDefault is a special value to select the default rate limit interval.
APIIntervalRateLimitDefault = 0
)

type attestationStatus string

const (
attestationStatusUnspecified attestationStatus = "NOTARIZATION_STATUS_UNSPECIFIED"
attestationStatusPending attestationStatus = "NOTARIZATION_STATUS_PENDING"
attestationStatusSubmitted attestationStatus = "NOTARIZATION_STATUS_SUBMITTED"
attestationStatusSessionApproved attestationStatus = "NOTARIZATION_STATUS_SESSION_APPROVED"
attestationStatusFailed attestationStatus = "NOTARIZATION_STATUS_FAILED"
)

var (
ErrUnknownResponse = errors.New("unexpected response from attestation API")
)

type TokenDataReader struct {
lggr logger.Logger
lbtcReader ccipdata.LBTCReader
httpClient http.IHttpClient
attestationApi *url.URL
attestationApiTimeout time.Duration
lbtcTokenAddress common.Address
rate *rate.Limiter

// coolDownUntil defines whether requests are blocked or not.
coolDownUntil time.Time
coolDownMu *sync.RWMutex
}

type messageAttestationResponse struct {
MessageHash string `json:"message_hash"`
Status attestationStatus `json:"status"`
Attestation string `json:"attestation"`
}

// TODO: Adjust after checking API docs
type attestationResponse struct {
Attestations []messageAttestationResponse `json:"attestations"`
}

// TODO: Implement encoding/decoding

var _ tokendata.Reader = &TokenDataReader{}

func NewLBTCTokenDataReader(
lggr logger.Logger,
lbtcReader ccipdata.LBTCReader,
lbtcAttestationApi *url.URL,
lbtcAttestationApiTimeoutSeconds int,
lbtcTokenAddress common.Address,
requestInterval time.Duration,
) *TokenDataReader {
timeout := time.Duration(lbtcAttestationApiTimeoutSeconds) * time.Second
if lbtcAttestationApiTimeoutSeconds == 0 {
timeout = defaultAttestationTimeout
}

if requestInterval == APIIntervalRateLimitDisabled {
requestInterval = 0
} else if requestInterval == APIIntervalRateLimitDefault {
requestInterval = defaultRequestInterval
}

return &TokenDataReader{
lggr: lggr,
lbtcReader: lbtcReader,
httpClient: http.NewObservedIHttpClient(&http.HttpClient{}),
attestationApi: lbtcAttestationApi,
attestationApiTimeout: timeout,
lbtcTokenAddress: lbtcTokenAddress,
coolDownMu: &sync.RWMutex{},
rate: rate.NewLimiter(rate.Every(requestInterval), 1),
}
}

func NewLBTCTokenDataReaderWithHttpClient(
origin TokenDataReader,
httpClient http.IHttpClient,
lbtcTokenAddress common.Address,
requestInterval time.Duration,
) *TokenDataReader {
return &TokenDataReader{
lggr: origin.lggr,
lbtcReader: origin.lbtcReader,
httpClient: httpClient,
attestationApi: origin.attestationApi,
attestationApiTimeout: origin.attestationApiTimeout,
coolDownMu: origin.coolDownMu,
lbtcTokenAddress: lbtcTokenAddress,
rate: rate.NewLimiter(rate.Every(requestInterval), 1),
}
}

// ReadTokenData queries the LBTC attestation API.
func (s *TokenDataReader) ReadTokenData(ctx context.Context, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, tokenIndex int) ([]byte, error) {
// TODO: Implement
return nil, nil
}

func (s *TokenDataReader) callAttestationApi(ctx context.Context, usdcMessageHash [32]byte) (attestationResponse, error) {
// TODO: Implement after checking API docs
return attestationResponse{}, nil
}

func (s *TokenDataReader) setCoolDownPeriod(d time.Duration) {
s.coolDownMu.Lock()
if d > maxCoolDownDuration {
d = maxCoolDownDuration
}
s.coolDownUntil = time.Now().Add(d)
s.coolDownMu.Unlock()
}

func (s *TokenDataReader) inCoolDownPeriod() bool {
s.coolDownMu.RLock()
defer s.coolDownMu.RUnlock()
return time.Now().Before(s.coolDownUntil)
}

func (s *TokenDataReader) Close() error {
return nil
}
5 changes: 5 additions & 0 deletions core/services/relay/evm/exec_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type SrcExecProvider struct {

feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider

// TODO: Add lbtc reader & api fields

// these values are nil and are updated for Close()
seenOnRampAddress *cciptypes.Address
seenSourceChainSelector *uint64
Expand Down Expand Up @@ -74,6 +76,8 @@ func NewSrcExecProvider(
}
}

// TODO: Initialize lbtc reader

return &SrcExecProvider{
lggr: lggr,
versionFinder: versionFinder,
Expand Down Expand Up @@ -195,6 +199,7 @@ func (s *SrcExecProvider) NewPriceRegistryReader(ctx context.Context, addr ccipt
return
}

// TODO: refactor to handle lbtc tokens. Separate methods.
func (s *SrcExecProvider) NewTokenDataReader(ctx context.Context, tokenAddress cciptypes.Address) (tokenDataReader cciptypes.TokenDataReader, err error) {
attestationURI, err2 := url.ParseRequestURI(s.usdcAttestationAPI)
if err2 != nil {
Expand Down

0 comments on commit 0be81b0

Please sign in to comment.