Skip to content

Commit

Permalink
Merge branch 'main' into solana/mcm-clear-root
Browse files Browse the repository at this point in the history
  • Loading branch information
jadepark-dev authored Dec 19, 2024
2 parents 72eea70 + 8c8e37e commit 379eb5a
Show file tree
Hide file tree
Showing 16 changed files with 543 additions and 50 deletions.
3 changes: 2 additions & 1 deletion commit/chainfee/observation.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ func (p *processor) Observation(
return Observation{}, err
}

supportedChains.Remove(p.destChain)
if supportedChains.Cardinality() == 0 {
p.lggr.Info("no supported chains, nothing to observe")
p.lggr.Info("no supported chains other than dest chain to observe")
return Observation{}, nil
}

Expand Down
50 changes: 39 additions & 11 deletions commit/chainfee/observation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package chainfee
import (
"math/big"
"math/rand"
"sort"
"testing"
"time"

"golang.org/x/exp/maps"

mapset "github.com/deckarep/golang-set/v2"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/types"
Expand All @@ -32,11 +35,15 @@ func Test_processor_Observation(t *testing.T) {
fChain map[ccipocr3.ChainSelector]int
expectedChainFeePriceUpdates map[ccipocr3.ChainSelector]Update

expErr bool
dstChain ccipocr3.ChainSelector

expErr bool
emptyObs bool
}{
{
name: "two chains",
supportedChains: []ccipocr3.ChainSelector{1},
name: "two chains excluding dest",
supportedChains: []ccipocr3.ChainSelector{1, 2, 3},
dstChain: 3,
chainFeeComponents: map[ccipocr3.ChainSelector]types.ChainFeeComponents{
1: {
ExecutionFee: big.NewInt(10),
Expand Down Expand Up @@ -86,9 +93,16 @@ func Test_processor_Observation(t *testing.T) {
fChain: map[ccipocr3.ChainSelector]int{
1: 1,
2: 2,
3: 1,
},
expErr: false,
},
{
name: "only dest chain",
supportedChains: []ccipocr3.ChainSelector{1},
dstChain: 1,
emptyObs: true,
},
}

for _, tc := range testCases {
Expand All @@ -103,25 +117,32 @@ func Test_processor_Observation(t *testing.T) {
p := &processor{
lggr: lggr,
chainSupport: cs,
destChain: tc.dstChain,
ccipReader: ccipReader,
oracleID: oracleID,
homeChain: homeChain,
metricsReporter: NoopMetrics{},
}

supportedSet := mapset.NewSet(tc.supportedChains...)
cs.EXPECT().DestChain().Return(tc.dstChain).Maybe()
cs.EXPECT().SupportedChains(oracleID).
Return(mapset.NewSet(tc.supportedChains...), nil)
Return(supportedSet, nil).Maybe()

supportedSet.Remove(tc.dstChain)
slicesWithoutDst := supportedSet.ToSlice()
sort.Slice(slicesWithoutDst, func(i, j int) bool { return slicesWithoutDst[i] < slicesWithoutDst[j] })

ccipReader.EXPECT().GetChainsFeeComponents(ctx, tc.supportedChains).
Return(tc.chainFeeComponents)
ccipReader.EXPECT().GetChainsFeeComponents(ctx, slicesWithoutDst).
Return(tc.chainFeeComponents).Maybe()

ccipReader.EXPECT().GetWrappedNativeTokenPriceUSD(ctx, tc.supportedChains).
Return(tc.nativeTokenPrices)
ccipReader.EXPECT().GetWrappedNativeTokenPriceUSD(ctx, slicesWithoutDst).
Return(tc.nativeTokenPrices).Maybe()

ccipReader.EXPECT().GetChainFeePriceUpdate(ctx, tc.supportedChains).
Return(tc.existingChainFeePriceUpdates)
ccipReader.EXPECT().GetChainFeePriceUpdate(ctx, slicesWithoutDst).
Return(tc.existingChainFeePriceUpdates).Maybe()

homeChain.EXPECT().GetFChain().Return(tc.fChain, nil)
homeChain.EXPECT().GetFChain().Return(tc.fChain, nil).Maybe()

tStart := time.Now()
obs, err := p.Observation(ctx, Outcome{}, Query{})
Expand All @@ -130,13 +151,20 @@ func Test_processor_Observation(t *testing.T) {
require.Error(t, err)
return
}
if tc.emptyObs {
require.Empty(t, obs)
return
}

require.NoError(t, err)
require.GreaterOrEqual(t, obs.TimestampNow.UnixNano(), tStart.UnixNano())
require.LessOrEqual(t, obs.TimestampNow.UnixNano(), tEnd.UnixNano())
require.Equal(t, tc.chainFeeComponents, obs.FeeComponents)
require.ElementsMatch(t, slicesWithoutDst, maps.Keys(obs.FeeComponents))
require.Equal(t, tc.nativeTokenPrices, obs.NativeTokenPrices)
require.ElementsMatch(t, slicesWithoutDst, maps.Keys(obs.NativeTokenPrices))
require.Equal(t, tc.expectedChainFeePriceUpdates, obs.ChainFeeUpdates)
require.ElementsMatch(t, slicesWithoutDst, maps.Keys(obs.ChainFeeUpdates))
require.Equal(t, tc.fChain, obs.FChain)
})
}
Expand Down
7 changes: 5 additions & 2 deletions commit/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,17 @@ func (p *PluginFactory) NewReportingPlugin(ctx context.Context, config ocr3types
oracleIDToP2PID[commontypes.OracleID(oracleID)] = node.P2pID
}

// map types to the facade.
// Map contract readers to ContractReaderFacade:
// - Extended reader adds finality violation and contract binding management.
// - Observed reader adds metric reporting.
readers := make(map[cciptypes.ChainSelector]contractreader.ContractReaderFacade, len(p.contractReaders))
for chain, cr := range p.contractReaders {
chainID, err1 := sel.GetChainIDFromSelector(uint64(chain))
if err1 != nil {
return nil, ocr3types.ReportingPluginInfo{}, fmt.Errorf("failed to get chain id from selector: %w", err1)
}
readers[chain] = contractreader.NewObserverReader(cr, lggr, chainID)
readers[chain] = contractreader.NewExtendedContractReader(
contractreader.NewObserverReader(cr, lggr, chainID))
}

// Bind the RMNHome contract
Expand Down
43 changes: 18 additions & 25 deletions commit/plugin_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,11 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
nodes := make([]ocr3types.ReportingPlugin[[]byte], len(oracleIDs))

newFeeComponents, newNativePrice, packedGasPrice := newRandomFees()
expectedChainFeeOutcome := chainfee.Outcome{
expectedChain1FeeOutcome := chainfee.Outcome{
GasPrices: []ccipocr3.GasPriceChain{
{
GasPrice: packedGasPrice,
ChainSel: destChain,
ChainSel: sourceChain1,
},
},
}
Expand All @@ -464,10 +464,6 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
MerkleRootOutcome: merkleOutcome,
ChainFeeOutcome: chainfee.Outcome{
GasPrices: []ccipocr3.GasPriceChain{
{
GasPrice: packedGasPrice,
ChainSel: destChain,
},
{
GasPrice: packedGasPrice,
ChainSel: sourceChain1,
Expand All @@ -485,15 +481,13 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
GetChainsFeeComponents(params.ctx, mock.Anything).
Return(
map[ccipocr3.ChainSelector]types.ChainFeeComponents{
destChain: newFeeComponents,
sourceChain1: newFeeComponents,
sourceChain2: newFeeComponents,
})

m.EXPECT().
GetWrappedNativeTokenPriceUSD(params.ctx, mock.Anything).
Return(map[ccipocr3.ChainSelector]ccipocr3.BigInt{
destChain: newNativePrice,
sourceChain1: newNativePrice,
sourceChain2: newNativePrice,
})
Expand All @@ -504,21 +498,20 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
prevOutcome: committypes.Outcome{},
expOutcome: committypes.Outcome{
MerkleRootOutcome: merkleOutcome,
ChainFeeOutcome: expectedChainFeeOutcome,
ChainFeeOutcome: expectedChain1FeeOutcome,
},
expTransmittedReportLen: 1,
mockCCIPReader: func(m *readerpkg_mock.MockCCIPReader) {
m.EXPECT().
GetChainsFeeComponents(params.ctx, mock.Anything).
Return(
map[ccipocr3.ChainSelector]types.ChainFeeComponents{
destChain: newFeeComponents,
sourceChain1: newFeeComponents,
})

m.EXPECT().
GetWrappedNativeTokenPriceUSD(params.ctx, mock.Anything).
Return(map[ccipocr3.ChainSelector]ccipocr3.BigInt{
destChain: newNativePrice,
sourceChain1: newNativePrice,
sourceChain2: newNativePrice,
})
Expand All @@ -528,41 +521,41 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
name: "fee components should not be updated within deviation",
prevOutcome: committypes.Outcome{
MerkleRootOutcome: merkleOutcome,
ChainFeeOutcome: expectedChainFeeOutcome,
ChainFeeOutcome: expectedChain1FeeOutcome,
},
expOutcome: committypes.Outcome{
MerkleRootOutcome: noReportMerkleOutcome(params.rmnReportCfg),
ChainFeeOutcome: expectedChainFeeOutcome,
ChainFeeOutcome: expectedChain1FeeOutcome,
},
expTransmittedReportLen: 1,
mockCCIPReader: func(m *readerpkg_mock.MockCCIPReader) {
m.EXPECT().
GetChainsFeeComponents(params.ctx, mock.Anything).
Return(
map[ccipocr3.ChainSelector]types.ChainFeeComponents{
destChain: newFeeComponents,
sourceChain1: newFeeComponents,
})

m.EXPECT().
GetWrappedNativeTokenPriceUSD(params.ctx, mock.Anything).
Return(map[ccipocr3.ChainSelector]ccipocr3.BigInt{
destChain: newNativePrice,
sourceChain1: newNativePrice,
})
},
},
{
name: "fresh fees (timestamped) should not be updated, even outside of deviation",
prevOutcome: committypes.Outcome{
MerkleRootOutcome: merkleOutcome,
ChainFeeOutcome: expectedChainFeeOutcome,
ChainFeeOutcome: expectedChain1FeeOutcome,
},
expOutcome: committypes.Outcome{
MerkleRootOutcome: noReportMerkleOutcome(params.rmnReportCfg),
ChainFeeOutcome: chainfee.Outcome{
GasPrices: []ccipocr3.GasPriceChain{
{
GasPrice: newPackedGasPrice2,
ChainSel: destChain,
ChainSel: sourceChain1,
},
},
},
Expand All @@ -573,29 +566,29 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
GetChainsFeeComponents(params.ctx, mock.Anything).
Return(
map[ccipocr3.ChainSelector]types.ChainFeeComponents{
destChain: newFeeComponents2,
sourceChain1: newFeeComponents2,
})

m.EXPECT().
GetWrappedNativeTokenPriceUSD(params.ctx, mock.Anything).
Return(map[ccipocr3.ChainSelector]ccipocr3.BigInt{
destChain: newNativePrice2,
sourceChain1: newNativePrice2,
})
},
},
{
name: "stale fees should be updated",
prevOutcome: committypes.Outcome{
MerkleRootOutcome: merkleOutcome,
ChainFeeOutcome: expectedChainFeeOutcome,
ChainFeeOutcome: expectedChain1FeeOutcome,
},
expOutcome: committypes.Outcome{
MerkleRootOutcome: noReportMerkleOutcome(params.rmnReportCfg),
ChainFeeOutcome: chainfee.Outcome{
GasPrices: []ccipocr3.GasPriceChain{
{
GasPrice: newPackedGasPrice2,
ChainSel: destChain,
ChainSel: sourceChain1,
},
},
},
Expand All @@ -606,12 +599,12 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
GetChainsFeeComponents(params.ctx, mock.Anything).
Return(
map[ccipocr3.ChainSelector]types.ChainFeeComponents{
destChain: newFeeComponents2,
sourceChain1: newFeeComponents2,
})
m.EXPECT().
GetWrappedNativeTokenPriceUSD(params.ctx, mock.Anything).
Return(map[ccipocr3.ChainSelector]ccipocr3.BigInt{
destChain: newNativePrice2,
sourceChain1: newNativePrice2,
})

m.EXPECT().GetChainFeePriceUpdate(params.ctx, mock.Anything).Unset()
Expand All @@ -621,9 +614,9 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) {
m.EXPECT().
GetChainFeePriceUpdate(params.ctx, mock.Anything).
Return(map[ccipocr3.ChainSelector]plugintypes.TimestampedBig{
destChain: {
sourceChain1: {
Timestamp: t,
Value: expectedChainFeeOutcome.GasPrices[0].GasPrice,
Value: expectedChain1FeeOutcome.GasPrices[0].GasPrice,
},
})
},
Expand Down
7 changes: 5 additions & 2 deletions execute/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,17 @@ func (p PluginFactory) NewReportingPlugin(
oracleIDToP2PID[commontypes.OracleID(oracleID)] = node.P2pID
}

// map types to the facade.
// Map contract readers to ContractReaderFacade:
// - Extended reader adds finality violation and contract binding management.
// - Observed reader adds metric reporting.
readers := make(map[cciptypes.ChainSelector]contractreader.ContractReaderFacade)
for chain, cr := range p.contractReaders {
chainID, err1 := sel.GetChainIDFromSelector(uint64(chain))
if err1 != nil {
return nil, ocr3types.ReportingPluginInfo{}, fmt.Errorf("failed to get chain id from selector: %w", err1)
}
readers[chain] = contractreader.NewObserverReader(cr, lggr, chainID)
readers[chain] = contractreader.NewExtendedContractReader(
contractreader.NewObserverReader(cr, lggr, chainID))
}

ccipReader := readerpkg.NewCCIPChainReader(
Expand Down
2 changes: 1 addition & 1 deletion internal/plugincommon/chain_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (c ccipChainSupport) SupportedChains(oracleID commontypes.OracleID) (mapset
return mapset.NewSet[cciptypes.ChainSelector](), fmt.Errorf("error getting supported chains: %w", err)
}

return supportedChains, nil
return supportedChains.Clone(), nil
}

// SupportsDestChain returns true if the given oracle supports the dest chain, returns false otherwise
Expand Down
47 changes: 47 additions & 0 deletions mocks/pkg/contractreader/contract_reader_facade.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 379eb5a

Please sign in to comment.