Skip to content

Commit

Permalink
Ocr3config/return active candidate (#285)
Browse files Browse the repository at this point in the history
* return active candidate type from home chain reader

* broken test

* fix broken test. We no longer need to call those functions

* fix exports

* make CI depend on specific branch of chainlink repo

* address comments

* comments

* fix unit tests, format

* set CI to use develop branch

* import naming
  • Loading branch information
0xAustinWang authored Nov 4, 2024
1 parent cd79a0d commit 4b7e196
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 58 deletions.
12 changes: 10 additions & 2 deletions commit/plugin_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"testing"
"time"

ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"

"github.com/smartcontractkit/chainlink-ccip/internal/libs/mathslib"
"github.com/smartcontractkit/chainlink-ccip/internal/libs/testhelpers/rand"

Expand Down Expand Up @@ -721,7 +723,11 @@ func setupNode(params SetupNodeParams, nodeID commontypes.OracleID) nodeSetup {
homeChainReader.EXPECT().GetFChain().Return(fChain, nil)
homeChainReader.EXPECT().
GetOCRConfigs(mock.Anything, params.donID, consts.PluginTypeCommit).
Return([]reader.OCR3ConfigWithMeta{{}}, nil).Maybe()
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{
ConfigDigest: params.reportingCfg.ConfigDigest,
},
}, nil).Maybe()

for peerID, supportedChains := range supportedChainsForPeer {
homeChainReader.EXPECT().GetSupportedChainsForPeer(peerID).Return(supportedChains, nil).Maybe()
Expand Down Expand Up @@ -825,6 +831,8 @@ func defaultNodeParams(t *testing.T) SetupNodeParams {
lggr := logger.Test(t)

donID := uint32(1)
rb := rand.RandomBytes32()
digest := ocrtypes.ConfigDigest(rb[:])

require.Equal(t, len(oracleIDs), len(peerIDs))

Expand Down Expand Up @@ -864,7 +872,7 @@ func defaultNodeParams(t *testing.T) SetupNodeParams {
PriceFeedChainSelector: sourceChain1,
}

reportingCfg := ocr3types.ReportingPluginConfig{F: 1}
reportingCfg := ocr3types.ReportingPluginConfig{F: 1, ConfigDigest: digest}

params := SetupNodeParams{
ctx: ctx,
Expand Down
4 changes: 2 additions & 2 deletions commit/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (p *Plugin) ShouldTransmitAcceptedReport(
}

// we only transmit reports if we are the "active" instance.
// we can check this by reading the OCR conigs home chain.
// we can check this by reading the OCR configs from the home chain.
isCandidate, err := p.isCandidateInstance(ctx)
if err != nil {
return false, fmt.Errorf("isCandidateInstance: %w", err)
Expand Down Expand Up @@ -171,5 +171,5 @@ func (p *Plugin) isCandidateInstance(ctx context.Context) (bool, error) {
return false, fmt.Errorf("failed to get ocr configs from home chain: %w", err)
}

return len(ocrConfigs) == 2 && ocrConfigs[1].ConfigDigest == p.reportingCfg.ConfigDigest, nil
return ocrConfigs.CandidateConfig.ConfigDigest == p.reportingCfg.ConfigDigest, nil
}
137 changes: 137 additions & 0 deletions commit/report_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
package commit

import (
"fmt"
"testing"

"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/smartcontractkit/chainlink-ccip/internal/libs/testhelpers/rand"
reader_mock "github.com/smartcontractkit/chainlink-ccip/mocks/internal_/reader"
"github.com/smartcontractkit/chainlink-ccip/pkg/consts"

"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"

"github.com/smartcontractkit/libocr/offchainreporting2plus/types"

"github.com/smartcontractkit/chainlink-ccip/commit/chainfee"
"github.com/smartcontractkit/chainlink-ccip/commit/merkleroot"
rmntypes "github.com/smartcontractkit/chainlink-ccip/commit/merkleroot/rmn/types"
"github.com/smartcontractkit/chainlink-ccip/commit/tokenprice"
"github.com/smartcontractkit/chainlink-ccip/internal/mocks"
"github.com/smartcontractkit/chainlink-ccip/internal/reader"
"github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
)

Expand Down Expand Up @@ -153,3 +165,128 @@ func TestPluginReports_InvalidOutcome(t *testing.T) {
_, err := p.Reports(tests.Context(t), 0, []byte("invalid json"))
require.Error(t, err)
}

func Test_IsCandidateCheck(t *testing.T) {
rb := rand.RandomBytes32()
digest := types.ConfigDigest(rb[:])
donID := uint32(3)
allTests := []struct {
name string
makePlugin func(t *testing.T, hc *reader_mock.MockHomeChain) *Plugin
makeHomeChain func(t *testing.T) *reader_mock.MockHomeChain
wantOutput bool
wantError bool
}{
{
name: "Should return true if digest matches",
makePlugin: func(t *testing.T, hc *reader_mock.MockHomeChain) *Plugin {
p := &Plugin{
homeChain: hc,
reportingCfg: ocr3types.ReportingPluginConfig{
ConfigDigest: digest,
},
}
return p
},
makeHomeChain: func(t *testing.T) *reader_mock.MockHomeChain {
h := reader_mock.NewMockHomeChain(t)
h.On("GetOCRConfigs", mock.Anything, mock.Anything, consts.PluginTypeCommit).
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{},
CandidateConfig: reader.OCR3ConfigWithMeta{
ConfigDigest: digest,
},
}, nil)
return h
},
wantOutput: true,
wantError: false,
},
{
name: "Should return false if digest doesn't match",
makePlugin: func(t *testing.T, hc *reader_mock.MockHomeChain) *Plugin {
p := &Plugin{
homeChain: hc,
reportingCfg: ocr3types.ReportingPluginConfig{
ConfigDigest: types.ConfigDigest(rand.RandomBytes32()),
},
}
return p
},
makeHomeChain: func(t *testing.T) *reader_mock.MockHomeChain {
h := reader_mock.NewMockHomeChain(t)
h.On("GetOCRConfigs", mock.Anything, mock.Anything, consts.PluginTypeCommit).
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{},
CandidateConfig: reader.OCR3ConfigWithMeta{
ConfigDigest: digest,
},
}, nil)
return h
},
wantOutput: false,
wantError: false,
},
{
name: "Should work as expected without candidate instance",
makePlugin: func(t *testing.T, hc *reader_mock.MockHomeChain) *Plugin {
p := &Plugin{
homeChain: hc,
reportingCfg: ocr3types.ReportingPluginConfig{
ConfigDigest: types.ConfigDigest(rand.RandomBytes32()),
},
}
return p
},
makeHomeChain: func(t *testing.T) *reader_mock.MockHomeChain {
h := reader_mock.NewMockHomeChain(t)
h.On("GetOCRConfigs", mock.Anything, mock.Anything, consts.PluginTypeCommit).
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{},
CandidateConfig: reader.OCR3ConfigWithMeta{},
}, nil)
return h
},
wantOutput: false,
wantError: false,
},
{
name: "Should throw error if donID doesn't exist",
makePlugin: func(t *testing.T, hc *reader_mock.MockHomeChain) *Plugin {
p := &Plugin{
homeChain: hc,
donID: donID,
reportingCfg: ocr3types.ReportingPluginConfig{
ConfigDigest: types.ConfigDigest(rand.RandomBytes32()),
},
}
return p
},
makeHomeChain: func(t *testing.T) *reader_mock.MockHomeChain {
h := reader_mock.NewMockHomeChain(t)
h.On("GetOCRConfigs", mock.Anything, donID, consts.PluginTypeCommit).
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{},
CandidateConfig: reader.OCR3ConfigWithMeta{},
}, fmt.Errorf("DonID does not exist"))
return h
},
wantOutput: false,
wantError: true,
},
}
for _, tt := range allTests {
t.Run(tt.name, func(t *testing.T) {
ctx := tests.Context(t)
hc := tt.makeHomeChain(t)
p := tt.makePlugin(t, hc)
actualOutput, actualError := p.isCandidateInstance(ctx)
assert.Equal(t, tt.wantOutput, actualOutput)
if tt.wantError {
require.Error(t, actualError)
} else {
require.NoError(t, actualError)
}
})
}
}
16 changes: 8 additions & 8 deletions execute/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,15 @@ func (p *Plugin) ShouldTransmitAcceptedReport(
return false, nil
}

// we only transmit reports if we are the "blue" instance.
// we can check this by reading the OCR conigs home chain.
isGreen, err := p.isGreenInstance(ctx)
// we only transmit reports if we are the "active" instance.
// we can check this by reading the OCR configs home chain.
isCandidate, err := p.isCandidateInstance(ctx)
if err != nil {
return false, fmt.Errorf("ShouldTransmitAcceptedReport.isGreenInstance: %w", err)
return false, fmt.Errorf("ShouldTransmitAcceptedReport.isCandidateInstance: %w", err)
}

if isGreen {
p.lggr.Debugw("not the blue instance, skipping report transmission",
if isCandidate {
p.lggr.Debugw("not the active instance, skipping report transmission",
"myDigest", p.reportingCfg.ConfigDigest.Hex())
return false, nil
}
Expand All @@ -324,13 +324,13 @@ func (p *Plugin) ShouldTransmitAcceptedReport(
return true, nil
}

func (p *Plugin) isGreenInstance(ctx context.Context) (bool, error) {
func (p *Plugin) isCandidateInstance(ctx context.Context) (bool, error) {
ocrConfigs, err := p.homeChain.GetOCRConfigs(ctx, p.donID, consts.PluginTypeExecute)
if err != nil {
return false, fmt.Errorf("failed to get ocr configs from home chain: %w", err)
}

return len(ocrConfigs) == 2 && ocrConfigs[1].ConfigDigest == p.reportingCfg.ConfigDigest, nil
return ocrConfigs.CandidateConfig.ConfigDigest == p.reportingCfg.ConfigDigest, nil
}

func (p *Plugin) Close() error {
Expand Down
33 changes: 21 additions & 12 deletions execute/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"testing"
"time"

"github.com/smartcontractkit/chainlink-ccip/internal/libs/testhelpers/rand"

mapset "github.com/deckarep/golang-set/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand All @@ -24,11 +26,10 @@ import (
"github.com/smartcontractkit/chainlink-ccip/execute/exectypes"
"github.com/smartcontractkit/chainlink-ccip/internal/libs/slicelib"
dt "github.com/smartcontractkit/chainlink-ccip/internal/plugincommon/discovery/discoverytypes"
"github.com/smartcontractkit/chainlink-ccip/internal/reader"
reader_mock "github.com/smartcontractkit/chainlink-ccip/mocks/internal_/reader"
readerpkg_mock "github.com/smartcontractkit/chainlink-ccip/mocks/pkg/reader"
codec_mocks "github.com/smartcontractkit/chainlink-ccip/mocks/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink-ccip/pkg/consts"
"github.com/smartcontractkit/chainlink-ccip/pkg/reader"
cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
plugintypes2 "github.com/smartcontractkit/chainlink-ccip/plugintypes"
)
Expand Down Expand Up @@ -531,12 +532,16 @@ func TestPlugin_ShouldTransmitAcceptReport_Ineligible(t *testing.T) {

func TestPlugin_ShouldTransmitAcceptReport_DecodeFailure(t *testing.T) {
const donID = uint32(1)
rb := rand.RandomBytes32()
digest := types.ConfigDigest(rb[:])
homeChain := reader_mock.NewMockHomeChain(t)
homeChain.On("GetSupportedChainsForPeer", mock.Anything).Return(mapset.NewSet(cciptypes.ChainSelector(1)), nil)
homeChain.
EXPECT().
GetOCRConfigs(mock.Anything, donID, consts.PluginTypeExecute).
Return([]reader.OCR3ConfigWithMeta{{}}, nil)
homeChain.On("GetOCRConfigs", mock.Anything, mock.Anything, mock.Anything).
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{
ConfigDigest: digest,
},
}, nil)
codec := codec_mocks.NewMockExecutePluginCodec(t)
codec.On("Decode", mock.Anything, mock.Anything).
Return(cciptypes.ExecutePluginReport{}, fmt.Errorf("test error"))
Expand All @@ -545,7 +550,7 @@ func TestPlugin_ShouldTransmitAcceptReport_DecodeFailure(t *testing.T) {
donID: donID,
lggr: logger.Test(t),
destChain: 1,
reportingCfg: ocr3types.ReportingPluginConfig{OracleID: 2},
reportingCfg: ocr3types.ReportingPluginConfig{OracleID: 2, ConfigDigest: digest},
reportCodec: codec,
homeChain: homeChain,
oracleIDToP2pID: map[commontypes.OracleID]libocrtypes.PeerID{
Expand All @@ -560,13 +565,17 @@ func TestPlugin_ShouldTransmitAcceptReport_DecodeFailure(t *testing.T) {

func TestPlugin_ShouldTransmitAcceptReport_Success(t *testing.T) {
const donID = uint32(1)
rb := rand.RandomBytes32()
digest := types.ConfigDigest(rb[:])
lggr, logs := logger.TestObserved(t, zapcore.DebugLevel)
homeChain := reader_mock.NewMockHomeChain(t)
homeChain.On("GetSupportedChainsForPeer", mock.Anything).Return(mapset.NewSet(cciptypes.ChainSelector(1)), nil)
homeChain.
EXPECT().
GetOCRConfigs(mock.Anything, donID, consts.PluginTypeExecute).
Return([]reader.OCR3ConfigWithMeta{{}}, nil)
homeChain.On("GetOCRConfigs", mock.Anything, mock.Anything, mock.Anything).
Return(reader.ActiveAndCandidate{
ActiveConfig: reader.OCR3ConfigWithMeta{
ConfigDigest: digest,
},
}, nil)
codec := codec_mocks.NewMockExecutePluginCodec(t)
codec.On("Decode", mock.Anything, mock.Anything).
Return(cciptypes.ExecutePluginReport{}, nil)
Expand All @@ -575,7 +584,7 @@ func TestPlugin_ShouldTransmitAcceptReport_Success(t *testing.T) {
donID: donID,
lggr: lggr,
destChain: 1,
reportingCfg: ocr3types.ReportingPluginConfig{OracleID: 2},
reportingCfg: ocr3types.ReportingPluginConfig{OracleID: 2, ConfigDigest: digest},
reportCodec: codec,
homeChain: homeChain,
oracleIDToP2pID: map[commontypes.OracleID]libocrtypes.PeerID{
Expand Down
10 changes: 7 additions & 3 deletions execute/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package execute

import (
"context"
crand "crypto/rand"
"encoding/binary"
"net/http"
"net/http/httptest"
Expand All @@ -14,6 +15,7 @@ import (

"github.com/smartcontractkit/libocr/commontypes"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"
ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types"

commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
Expand Down Expand Up @@ -288,10 +290,12 @@ func (it *IntTest) newNode(
N int,
) nodeSetup {
reportCodec := mocks.NewExecutePluginJSONReportCodec()

b := make([]byte, 32)
_, _ = crand.Read(b)
rCfg := ocr3types.ReportingPluginConfig{
N: N,
OracleID: commontypes.OracleID(id),
N: N,
OracleID: commontypes.OracleID(id),
ConfigDigest: ocrtypes.ConfigDigest(b),
}

node1 := NewPlugin(
Expand Down
Loading

0 comments on commit 4b7e196

Please sign in to comment.