Skip to content

Commit

Permalink
CCIP-4403 lbtc onchain reader (#1555)
Browse files Browse the repository at this point in the history
Epic: https://smartcontract-it.atlassian.net/browse/CCIP-3488
## Description
Added LBTC smart contract reader
  • Loading branch information
bukata-sa committed Dec 6, 2024
1 parent 484940a commit 9628956
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 70 deletions.
2 changes: 1 addition & 1 deletion core/services/ocr2/plugins/ccip/ccipexec/initializers.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func NewExecServices(ctx context.Context, lggr logger.Logger, jb job.Job, srcPro

lbtcReader, err2 := srcProvider.NewTokenDataReader(ctx, ccip.EvmAddrToGeneric(pluginConfig.LBTCConfig.SourceTokenAddress))
if err2 != nil {
return nil, fmt.Errorf("new usdc reader: %w", err2)
return nil, fmt.Errorf("new lbtc reader: %w", err2)
}
tokenDataProviders[cciptypes.Address(pluginConfig.LBTCConfig.SourceTokenAddress.String())] = lbtcReader
}
Expand Down
7 changes: 3 additions & 4 deletions core/services/ocr2/plugins/ccip/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,9 @@ type USDCConfig struct {
}

type LBTCConfig struct {
SourceTokenAddress common.Address
SourceMessageTransmitterAddress common.Address
AttestationAPI string
AttestationAPITimeoutSeconds uint
SourceTokenAddress common.Address
AttestationAPI string
AttestationAPITimeoutSeconds uint
// AttestationAPIIntervalMilliseconds can be set to -1 to disable or 0 to use a default interval.
AttestationAPIIntervalMilliseconds int
}
Expand Down
9 changes: 0 additions & 9 deletions core/services/ocr2/plugins/ccip/exportinternal.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,7 @@ func CloseUSDCReader(lggr logger.Logger, jobID string, transmitter common.Addres
return ccipdata.CloseUSDCReader(lggr, jobID, transmitter, lp)
}

func NewLBTCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller, registerFilters bool) (*ccipdata.LBTCReaderImpl, error) {
return ccipdata.NewLBTCReader(lggr, jobID, transmitter, lp, registerFilters)
}

func CloseLBTCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller) error {
return ccipdata.CloseLBTCReader(lggr, jobID, transmitter, lp)
}

type USDCReaderImpl = ccipdata.USDCReaderImpl
type LBTCReaderImpl = ccipdata.LBTCReaderImpl

var DefaultRpcBatchSizeLimit = rpclib.DefaultRpcBatchSizeLimit
var DefaultRpcBatchBackOffMultiplier = rpclib.DefaultRpcBatchBackOffMultiplier
Expand Down
23 changes: 0 additions & 23 deletions core/services/ocr2/plugins/ccip/internal/ccipdata/lbtc_reader.go

This file was deleted.

96 changes: 78 additions & 18 deletions core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package lbtc

import (
"bytes"
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"net/url"
"sync"
Expand All @@ -15,6 +17,7 @@ import (

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/abihelpers"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/http"
)
Expand Down Expand Up @@ -72,16 +75,52 @@ type TokenDataReader struct {
type messageAttestationResponse struct {
MessageHash string `json:"message_hash"`
Status attestationStatus `json:"status"`
Attestation string `json:"attestation"`
Attestation string `json:"attestation"` // Attestation represented by abi.encode(payload, proof)
}

// TODO: Adjust after checking API docs
type attestationRequest struct {
PayloadHashes []string `json:"messageHash"`
}

type attestationResponse struct {
Attestations []messageAttestationResponse `json:"attestations"`
}

// TODO: Implement encoding/decoding

type sourceTokenData struct {
SourcePoolAddress []byte
DestTokenAddress []byte
ExtraData []byte
DestGasAmount uint32
}

func (m sourceTokenData) AbiString() string {
return `[{
"components": [
{"name": "sourcePoolAddress", "type": "bytes"},
{"name": "destTokenAddress", "type": "bytes"},
{"name": "extraData", "type": "bytes"},
{"name": "destGasAmount", "type": "uint32"}
],
"type": "tuple"
}]`
}

func (m sourceTokenData) Validate() error {
if len(m.SourcePoolAddress) == 0 {
return errors.New("sourcePoolAddress must be non-empty")
}
if len(m.DestTokenAddress) == 0 {
return errors.New("destTokenAddress must be non-empty")
}
if len(m.ExtraData) == 0 {
return errors.New("extraData must be non-empty")
}
return nil
}

var _ tokendata.Reader = &TokenDataReader{}

func NewLBTCTokenDataReader(
Expand Down Expand Up @@ -149,17 +188,16 @@ func (s *TokenDataReader) ReadTokenData(ctx context.Context, msg cciptypes.EVM2E
}
}

messageBody, err := s.getLBTCMessageBody(ctx, msg, tokenIndex)
payloadHash, err := s.getLBTCPayloadHash(msg, tokenIndex)
if err != nil {
return []byte{}, errors.Wrap(err, "failed getting the LBTC message body")
}

msgID := hexutil.Encode(msg.MessageID[:])
messageBodyHash := sha256.Sum256(messageBody)
messageBodyHashHex := hexutil.Encode(messageBodyHash[:])
s.lggr.Infow("Calling attestation API", "messageBodyHash", messageBodyHashHex, "messageID", msgID)
payloadHashHex := hexutil.Encode(payloadHash[:])
s.lggr.Infow("Calling attestation API", "messageBodyHash", payloadHashHex, "messageID", msgID)

attestationResp, err := s.callAttestationApi(ctx, messageBodyHash)
attestationResp, err := s.callAttestationApi(ctx, payloadHash)
if err != nil {
return nil, err
}
Expand All @@ -171,19 +209,19 @@ func (s *TokenDataReader) ReadTokenData(ctx context.Context, msg cciptypes.EVM2E
}
var attestation messageAttestationResponse
for _, attestationCandidate := range attestationResp.Attestations {
if attestationCandidate.MessageHash == messageBodyHashHex {
if attestationCandidate.MessageHash == payloadHashHex {
attestation = attestationCandidate
}
}
s.lggr.Infow("Got response from attestation API", "messageID", msgID,
"attestationStatus", attestation.Status, "attestation", attestation)
switch attestation.Status {
case attestationStatusSessionApproved:
messageAndAttestation, err := encodeMessageAndAttestation(messageBody, attestation.Attestation)
payloadAndProof, err := hexutil.Decode(attestation.Attestation)
if err != nil {
return nil, fmt.Errorf("failed to encode messageAndAttestation : %w", err)
return nil, err
}
return messageAndAttestation, nil
return payloadAndProof, nil
case attestationStatusPending:
return nil, tokendata.ErrNotReady
case attestationStatusSubmitted:
Expand All @@ -194,24 +232,46 @@ func (s *TokenDataReader) ReadTokenData(ctx context.Context, msg cciptypes.EVM2E
}
}

func (s *TokenDataReader) getLBTCMessageBody(ctx context.Context, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, tokenIndex int) ([]byte, error) {
return nil, nil
func (s *TokenDataReader) getLBTCPayloadHash(msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, tokenIndex int) ([32]byte, error) {
decodedSourceTokenData, err := abihelpers.DecodeAbiStruct[sourceTokenData](msg.SourceTokenData[tokenIndex])
if err != nil {
return [32]byte{}, err
}
destTokenData := decodedSourceTokenData.ExtraData
var payloadHash [32]byte
// We don't have better way to determine if the extraData is a payload or sha256(payload)
// Last parameter of the payload struct is 32-bytes nonce (see Lombard's Bridge._deposit(...) method),
// so we can assume that payload always exceeds 32 bytes
if len(destTokenData) != 32 {
payloadHash = sha256.Sum256(destTokenData)
s.lggr.Warnw("SourceTokenData.extraData size is not 32. Probably this is deposit payload, not sha256(payload). "+
"This message was sent when LBTC attestation was disabled onchain. Will use sha256 from this value",
"destTokenData", destTokenData, "newPayloadHash", payloadHash)
} else {
payloadHash = [32]byte(destTokenData)
}
return payloadHash, nil
}

func (s *TokenDataReader) callAttestationApi(ctx context.Context, lbtcMessageHash [32]byte) (attestationResponse, error) {
_, _, _, err := s.httpClient.Get(ctx, "", s.attestationApiTimeout)
attestationUrl := fmt.Sprintf("%s/bridge/%s/%s", s.attestationApi.String(), apiVersion, attestationPath)
request := attestationRequest{PayloadHashes: []string{hexutil.Encode(lbtcMessageHash[:])}}
encodedRequest, err := json.Marshal(request)
requestBuffer := bytes.NewBuffer(encodedRequest)
if err != nil {
return attestationResponse{}, err
}
respRaw, _, _, err := s.httpClient.Post(ctx, attestationUrl, requestBuffer, s.attestationApiTimeout)
switch {
case errors.Is(err, tokendata.ErrRateLimit):
s.setCoolDownPeriod(defaultCoolDownDuration)
return attestationResponse{}, tokendata.ErrRateLimit
case err != nil:
return attestationResponse{}, err
}
return attestationResponse{}, nil
}

func encodeMessageAndAttestation(messageBody []byte, attestation string) ([]byte, error) {
return nil, nil
var attestationResp attestationResponse
err = json.Unmarshal(respRaw, &attestationResp)
return attestationResp, err
}

func (s *TokenDataReader) setCoolDownPeriod(d time.Duration) {
Expand Down
20 changes: 20 additions & 0 deletions core/services/ocr2/plugins/ccip/tokendata/lbtc/lbtc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package lbtc

import (
"testing"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
)

func Test_DecodeSourceTokenData(t *testing.T) {
input, err := hexutil.Decode("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000249f00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000267d40f64ecc4d95f3e8b2237df5f37b10812c250000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c47e4b3124597fdf8dd07843d4a7052f2ee80c3000000000000000000000000000000000000000000000000000000000000000e45c70a5050000000000000000000000000000000000000000000000000000000000aa36a7000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc0000000000000000000000000000000000000000000000000000000000014a34000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc00000000000000000000000062f10ce5b727edf787ea45776bd050308a61150800000000000000000000000000000000000000000000000000000000000003e6000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
decoded, err := abihelpers.DecodeAbiStruct[sourceTokenData](input)
require.NoError(t, err)
expected, err := hexutil.Decode("0x5c70a5050000000000000000000000000000000000000000000000000000000000aa36a7000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc0000000000000000000000000000000000000000000000000000000000014a34000000000000000000000000845f8e3c214d8d0e4d83fc094f302aa26a12a0bc00000000000000000000000062f10ce5b727edf787ea45776bd050308a61150800000000000000000000000000000000000000000000000000000000000003e60000000000000000000000000000000000000000000000000000000000000006")
require.NoError(t, err)
require.Equal(t, expected, decoded.ExtraData)
}
15 changes: 0 additions & 15 deletions core/services/relay/evm/exec_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ type SrcExecProvider struct {
maxGasPrice *big.Int
usdcReader *ccip.USDCReaderImpl
usdcConfig config.USDCConfig
lbtcReader *ccip.LBTCReaderImpl
lbtcConfig config.LBTCConfig

feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider
Expand Down Expand Up @@ -73,13 +72,6 @@ func NewSrcExecProvider(
return nil, fmt.Errorf("new usdc reader: %w", err)
}
}
var lbtcReader *ccip.LBTCReaderImpl
if lbtcConfig.AttestationAPI != "" {
lbtcReader, err = ccip.NewLBTCReader(lggr, jobID, lbtcConfig.SourceMessageTransmitterAddress, lp, true)
if err != nil {
return nil, fmt.Errorf("new usdc reader: %w", err)
}
}

return &SrcExecProvider{
lggr: lggr,
Expand All @@ -91,7 +83,6 @@ func NewSrcExecProvider(
startBlock: startBlock,
usdcReader: usdcReader,
usdcConfig: usdcConfig,
lbtcReader: lbtcReader,
lbtcConfig: lbtcConfig,
feeEstimatorConfig: feeEstimatorConfig,
}, nil
Expand Down Expand Up @@ -127,12 +118,6 @@ func (s *SrcExecProvider) Close() error {
}
return ccip.CloseUSDCReader(s.lggr, s.lggr.Name(), s.usdcConfig.SourceMessageTransmitterAddress, s.lp)
})
unregisterFuncs = append(unregisterFuncs, func() error {
if s.lbtcConfig.AttestationAPI == "" {
return nil
}
return ccip.CloseLBTCReader(s.lggr, s.lggr.Name(), s.lbtcConfig.SourceMessageTransmitterAddress, s.lp)
})
var multiErr error
for _, fn := range unregisterFuncs {
if err := fn(); err != nil {
Expand Down

0 comments on commit 9628956

Please sign in to comment.