Skip to content

Commit

Permalink
Merge branch 'ccip-develop' into feat/productionize-examples
Browse files Browse the repository at this point in the history
  • Loading branch information
jhweintraub authored Jul 1, 2024
2 parents e391d17 + c0b080a commit 5bfe40e
Show file tree
Hide file tree
Showing 8 changed files with 449 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-humans-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ccip": patch
---

Added tests and cleanup of liquidity graph #changed
3 changes: 1 addition & 2 deletions core/services/ocr2/plugins/liquiditymanager/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import (

// GraphWriter provides write access to the liquidity graph.
type GraphWriter interface {
// Add adds new data and connection to the graph.
Add(from, to Data) error
// AddEdges adds a list of edges to the graph.
AddEdges(edges []models.Edge) error
// SetLiquidity sets the liquidity of the provided network.
SetLiquidity(n models.NetworkSelector, liquidity *big.Int) bool
}
Expand Down
182 changes: 182 additions & 0 deletions core/services/ocr2/plugins/liquiditymanager/graph/reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package graph

import (
"math/big"
"testing"

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

"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/liquiditymanager/models"
)

func TestGrpah_NodeReaderGetters(t *testing.T) {
g := NewGraph()

data1 := Data{
Liquidity: big.NewInt(1),
TokenAddress: models.Address(common.HexToAddress("0x11")),
LiquidityManagerAddress: models.Address(common.HexToAddress("0x12")),
XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{},
ConfigDigest: models.ConfigDigest{
ConfigDigest: [32]byte{1},
},
NetworkSelector: models.NetworkSelector(1),
}
require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(1), data1))

tests := []struct {
name string
net models.NetworkSelector
data *Data
}{
{
name: "happy path",
net: models.NetworkSelector(1),
data: &data1,
},
{
name: "not exist",
net: models.NetworkSelector(333),
data: nil,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
liq, err := g.GetLiquidity(tc.net)
if tc.data == nil {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.data.Liquidity, liq)
}

tokenAddr, err := g.GetTokenAddress(tc.net)
if tc.data == nil {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.data.TokenAddress, tokenAddr)
}

liqManagerAddr, err := g.GetLiquidityManagerAddress(tc.net)
if tc.data == nil {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.data.LiquidityManagerAddress, liqManagerAddr)
}

xChainData, err := g.GetXChainLiquidityManagerData(tc.net)
if tc.data == nil {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.data.XChainLiquidityManagers, xChainData)
}

data, err := g.GetData(tc.net)
if tc.data == nil {
require.Error(t, err)
} else {
require.NoError(t, err)
require.True(t, tc.data.Equals(data))
}
})
}
}

func TestGraph_FindPath(t *testing.T) {
g := NewGraph()

data1 := Data{
Liquidity: big.NewInt(1),
TokenAddress: models.Address(common.HexToAddress("0x11")),
LiquidityManagerAddress: models.Address(common.HexToAddress("0x12")),
XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{},
ConfigDigest: models.ConfigDigest{
ConfigDigest: [32]byte{1},
},
NetworkSelector: models.NetworkSelector(1),
}
require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(1), data1))

data2 := Data{
Liquidity: big.NewInt(2),
TokenAddress: models.Address(common.HexToAddress("0x21")),
LiquidityManagerAddress: models.Address(common.HexToAddress("0x22")),
XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{},
ConfigDigest: models.ConfigDigest{
ConfigDigest: [32]byte{2},
},
NetworkSelector: models.NetworkSelector(2),
}
require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(2), data2))

data3 := Data{
Liquidity: big.NewInt(3),
TokenAddress: models.Address(common.HexToAddress("0x31")),
LiquidityManagerAddress: models.Address(common.HexToAddress("0x32")),
XChainLiquidityManagers: map[models.NetworkSelector]XChainLiquidityManagerData{},
ConfigDigest: models.ConfigDigest{
ConfigDigest: [32]byte{3},
},
NetworkSelector: models.NetworkSelector(3),
}
require.True(t, g.(GraphTest).AddNetwork(models.NetworkSelector(3), data3))

require.NoError(t, g.(GraphTest).AddConnection(models.NetworkSelector(1), models.NetworkSelector(2)))
require.NoError(t, g.(GraphTest).AddConnection(models.NetworkSelector(2), models.NetworkSelector(3)))

tests := []struct {
name string
from models.NetworkSelector
to models.NetworkSelector
maxEdges int
want []models.NetworkSelector
}{
{
name: "happy path 2 edges",
from: models.NetworkSelector(1),
to: models.NetworkSelector(3),
maxEdges: 2,
want: []models.NetworkSelector{models.NetworkSelector(2), models.NetworkSelector(3)},
},
{
name: "happy path 1 edge",
from: models.NetworkSelector(1),
to: models.NetworkSelector(2),
maxEdges: 1,
want: []models.NetworkSelector{models.NetworkSelector(2)},
},
{
name: "not enough edges",
from: models.NetworkSelector(1),
to: models.NetworkSelector(3),
maxEdges: 1,
want: []models.NetworkSelector{},
},
{
name: "no path",
from: models.NetworkSelector(2),
to: models.NetworkSelector(10),
want: []models.NetworkSelector{},
},
{
name: "same node",
from: models.NetworkSelector(1),
to: models.NetworkSelector(1),
want: []models.NetworkSelector{},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
path := g.FindPath(tc.from, tc.to, tc.maxEdges, func(nodes ...Data) bool {
return true
})
require.Equal(t, tc.want, path)
})
}
}
12 changes: 0 additions & 12 deletions core/services/ocr2/plugins/liquiditymanager/graph/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ func (g *liquidityGraph) Add(from, to Data) error {
return nil
}

func (g *liquidityGraph) AddEdges(edges []models.Edge) error {
g.lock.Lock()
defer g.lock.Unlock()

for _, edge := range edges {
if err := g.addConnection(edge.Source, edge.Dest); err != nil {
return fmt.Errorf("add connection %d -> %d: %w", edge.Source, edge.Dest, err)
}
}
return nil
}

func (g *liquidityGraph) SetLiquidity(n models.NetworkSelector, liquidity *big.Int) bool {
g.lock.Lock()
defer g.lock.Unlock()
Expand Down
133 changes: 124 additions & 9 deletions core/services/ocr3/plugins/ccipevm/commitcodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,137 @@ package ccipevm

import (
"context"
"fmt"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"

cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
)

var _ cciptypes.CommitPluginCodec = (*CommitPluginCodec)(nil)

type CommitPluginCodec struct{}
// CommitPluginCodecV1 is a codec for encoding and decoding commit plugin reports.
// Compatible with:
// - "EVM2EVMMultiOffRamp 1.6.0-dev"
type CommitPluginCodecV1 struct {
commitReportAcceptedEventInputs abi.Arguments
}

func NewCommitPluginCodec() *CommitPluginCodec {
return &CommitPluginCodec{}
func NewCommitPluginCodecV1() *CommitPluginCodecV1 {
abiParsed, err := abi.JSON(strings.NewReader(evm_2_evm_multi_offramp.EVM2EVMMultiOffRampABI))
if err != nil {
panic(fmt.Errorf("parse multi offramp abi: %s", err))
}
eventInputs := abihelpers.MustGetEventInputs("CommitReportAccepted", abiParsed)
return &CommitPluginCodecV1{commitReportAcceptedEventInputs: eventInputs}
}

func (c *CommitPluginCodec) Encode(ctx context.Context, report cciptypes.CommitPluginReport) ([]byte, error) {
panic("implement me")
func (c *CommitPluginCodecV1) Encode(ctx context.Context, report cciptypes.CommitPluginReport) ([]byte, error) {
merkleRoots := make([]evm_2_evm_multi_offramp.EVM2EVMMultiOffRampMerkleRoot, 0, len(report.MerkleRoots))
for _, root := range report.MerkleRoots {
merkleRoots = append(merkleRoots, evm_2_evm_multi_offramp.EVM2EVMMultiOffRampMerkleRoot{
SourceChainSelector: uint64(root.ChainSel),
Interval: evm_2_evm_multi_offramp.EVM2EVMMultiOffRampInterval{
Min: uint64(root.SeqNumsRange.Start()),
Max: uint64(root.SeqNumsRange.End()),
},
MerkleRoot: root.MerkleRoot,
})
}

tokenPriceUpdates := make([]evm_2_evm_multi_offramp.InternalTokenPriceUpdate, 0, len(report.PriceUpdates.TokenPriceUpdates))
for _, update := range report.PriceUpdates.TokenPriceUpdates {
if !common.IsHexAddress(string(update.TokenID)) {
return nil, fmt.Errorf("invalid token address: %s", update.TokenID)
}
if update.Price.IsEmpty() {
return nil, fmt.Errorf("empty price for token: %s", update.TokenID)
}
tokenPriceUpdates = append(tokenPriceUpdates, evm_2_evm_multi_offramp.InternalTokenPriceUpdate{
SourceToken: common.HexToAddress(string(update.TokenID)),
UsdPerToken: update.Price.Int,
})
}

gasPriceUpdates := make([]evm_2_evm_multi_offramp.InternalGasPriceUpdate, 0, len(report.PriceUpdates.GasPriceUpdates))
for _, update := range report.PriceUpdates.GasPriceUpdates {
if update.GasPrice.IsEmpty() {
return nil, fmt.Errorf("empty gas price for chain: %d", update.ChainSel)
}

gasPriceUpdates = append(gasPriceUpdates, evm_2_evm_multi_offramp.InternalGasPriceUpdate{
DestChainSelector: uint64(update.ChainSel),
UsdPerUnitGas: update.GasPrice.Int,
})
}

evmReport := evm_2_evm_multi_offramp.EVM2EVMMultiOffRampCommitReport{
PriceUpdates: evm_2_evm_multi_offramp.InternalPriceUpdates{
TokenPriceUpdates: tokenPriceUpdates,
GasPriceUpdates: gasPriceUpdates,
},
MerkleRoots: merkleRoots,
}

return c.commitReportAcceptedEventInputs.PackValues([]interface{}{evmReport})
}

func (c *CommitPluginCodec) Decode(ctx context.Context, bytes []byte) (cciptypes.CommitPluginReport, error) {
panic("implement me")
func (c *CommitPluginCodecV1) Decode(ctx context.Context, bytes []byte) (cciptypes.CommitPluginReport, error) {
unpacked, err := c.commitReportAcceptedEventInputs.Unpack(bytes)
if err != nil {
return cciptypes.CommitPluginReport{}, err
}
if len(unpacked) != 1 {
return cciptypes.CommitPluginReport{}, fmt.Errorf("expected 1 argument, got %d", len(unpacked))
}

commitReportRaw := abi.ConvertType(unpacked[0], new(evm_2_evm_multi_offramp.EVM2EVMMultiOffRampCommitReport))
commitReport, is := commitReportRaw.(*evm_2_evm_multi_offramp.EVM2EVMMultiOffRampCommitReport)
if !is {
return cciptypes.CommitPluginReport{},
fmt.Errorf("expected EVM2EVMMultiOffRampCommitReport, got %T", unpacked[0])
}

merkleRoots := make([]cciptypes.MerkleRootChain, 0, len(commitReport.MerkleRoots))
for _, root := range commitReport.MerkleRoots {
merkleRoots = append(merkleRoots, cciptypes.MerkleRootChain{
ChainSel: cciptypes.ChainSelector(root.SourceChainSelector),
SeqNumsRange: cciptypes.NewSeqNumRange(
cciptypes.SeqNum(root.Interval.Min),
cciptypes.SeqNum(root.Interval.Max),
),
MerkleRoot: root.MerkleRoot,
})
}

tokenPriceUpdates := make([]cciptypes.TokenPrice, 0, len(commitReport.PriceUpdates.TokenPriceUpdates))
for _, update := range commitReport.PriceUpdates.TokenPriceUpdates {
tokenPriceUpdates = append(tokenPriceUpdates, cciptypes.TokenPrice{
TokenID: types.Account(update.SourceToken.String()),
Price: cciptypes.NewBigInt(big.NewInt(0).Set(update.UsdPerToken)),
})
}

gasPriceUpdates := make([]cciptypes.GasPriceChain, 0, len(commitReport.PriceUpdates.GasPriceUpdates))
for _, update := range commitReport.PriceUpdates.GasPriceUpdates {
gasPriceUpdates = append(gasPriceUpdates, cciptypes.GasPriceChain{
GasPrice: cciptypes.NewBigInt(big.NewInt(0).Set(update.UsdPerUnitGas)),
ChainSel: cciptypes.ChainSelector(update.DestChainSelector),
})
}

return cciptypes.CommitPluginReport{
MerkleRoots: merkleRoots,
PriceUpdates: cciptypes.PriceUpdates{
TokenPriceUpdates: tokenPriceUpdates,
GasPriceUpdates: gasPriceUpdates,
},
}, nil
}

// Ensure CommitPluginCodec implements the CommitPluginCodec interface
var _ cciptypes.CommitPluginCodec = (*CommitPluginCodecV1)(nil)
Loading

0 comments on commit 5bfe40e

Please sign in to comment.