From 315746307aa7d87b3065c7b9991569ddc1f35cd7 Mon Sep 17 00:00:00 2001
From: Connor Stein <connor.stein@mail.mcgill.ca>
Date: Thu, 17 Oct 2024 17:41:58 -0400
Subject: [PATCH] Use deterministic OCR configuration (#14775)

* Misc

* Misc

* Pass in shared secret

* Both secrets deterministic

* Use ocr secrets

* Use dummy secrets

* Add chain test

* Add comment

* Sort imports
---
 .../deployment/ccip/add_chain.go              |  2 ++
 .../deployment/ccip/add_chain_test.go         |  3 ++-
 .../deployment/ccip/add_lane_test.go          |  2 ++
 .../ccip/changeset/initial_deploy_test.go     |  2 ++
 integration-tests/deployment/ccip/deploy.go   |  7 +++++++
 .../deployment/ccip/deploy_home_chain.go      |  8 +++++--
 .../deployment/ccip/deploy_test.go            |  1 +
 integration-tests/deployment/ccip/state.go    | 10 +++++++++
 integration-tests/deployment/environment.go   |  3 ++-
 integration-tests/deployment/helpers.go       | 21 +++++++++++++++++++
 integration-tests/deployment/multiclient.go   |  4 ++--
 integration-tests/smoke/ccip_test.go          |  3 +++
 12 files changed, 60 insertions(+), 6 deletions(-)

diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go
index 810cb77f6a9..72455dfdd19 100644
--- a/integration-tests/deployment/ccip/add_chain.go
+++ b/integration-tests/deployment/ccip/add_chain.go
@@ -25,6 +25,7 @@ import (
 // to connect the new chain to the existing chains.
 func NewChainInboundProposal(
 	e deployment.Environment,
+	ocrSecrets deployment.OCRSecrets,
 	state CCIPOnChainState,
 	homeChainSel uint64,
 	feedChainSel uint64,
@@ -132,6 +133,7 @@ func NewChainInboundProposal(
 
 	newDONArgs, err := BuildAddDONArgs(
 		e.Logger,
+		ocrSecrets,
 		state.Chains[newChainSel].OffRamp,
 		e.Chains[newChainSel],
 		feedChainSel,
diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go
index 26d9ad79885..5e05fff59ff 100644
--- a/integration-tests/deployment/ccip/add_chain_test.go
+++ b/integration-tests/deployment/ccip/add_chain_test.go
@@ -53,6 +53,7 @@ func TestAddChainInbound(t *testing.T) {
 		MCMSConfig:         NewTestMCMSConfig(t, e.Env),
 		FeeTokenContracts:  e.FeeTokenContracts,
 		CapabilityRegistry: state.Chains[e.HomeChainSel].CapabilityRegistry.Address(),
+		OCRSecrets:         deployment.XXXGenerateTestOCRSecrets(),
 	})
 	require.NoError(t, err)
 	state, err = LoadOnchainState(e.Env, e.Ab)
@@ -130,7 +131,7 @@ func TestAddChainInbound(t *testing.T) {
 
 	// Generate and sign inbound proposal to new 4th chain.
 	rmnHomeAddressBytes := common.HexToAddress(rmnHomeAddress).Bytes()
-	chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, e.FeedChainSel, newChain, initialDeploy, tokenConfig, rmnHomeAddressBytes)
+	chainInboundProposal, err := NewChainInboundProposal(e.Env, deployment.XXXGenerateTestOCRSecrets(), state, e.HomeChainSel, e.FeedChainSel, newChain, initialDeploy, tokenConfig, rmnHomeAddressBytes)
 	require.NoError(t, err)
 	chainInboundExec := SignProposal(t, e.Env, chainInboundProposal)
 	for _, sel := range initialDeploy {
diff --git a/integration-tests/deployment/ccip/add_lane_test.go b/integration-tests/deployment/ccip/add_lane_test.go
index 5a47b8f087f..e63d1845774 100644
--- a/integration-tests/deployment/ccip/add_lane_test.go
+++ b/integration-tests/deployment/ccip/add_lane_test.go
@@ -6,6 +6,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
+	"github.com/smartcontractkit/chainlink/integration-tests/deployment"
 	"github.com/smartcontractkit/chainlink/v2/core/logger"
 )
 
@@ -27,6 +28,7 @@ func TestAddLane(t *testing.T) {
 		MCMSConfig:         NewTestMCMSConfig(t, e.Env),
 		FeeTokenContracts:  e.FeeTokenContracts,
 		CapabilityRegistry: state.Chains[e.HomeChainSel].CapabilityRegistry.Address(),
+		OCRSecrets:         deployment.XXXGenerateTestOCRSecrets(),
 	})
 	require.NoError(t, err)
 
diff --git a/integration-tests/deployment/ccip/changeset/initial_deploy_test.go b/integration-tests/deployment/ccip/changeset/initial_deploy_test.go
index 12d794a66e3..6cb3bc12d2f 100644
--- a/integration-tests/deployment/ccip/changeset/initial_deploy_test.go
+++ b/integration-tests/deployment/ccip/changeset/initial_deploy_test.go
@@ -5,6 +5,7 @@ import (
 
 	cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
 	"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
+	"github.com/smartcontractkit/chainlink/integration-tests/deployment"
 
 	"github.com/stretchr/testify/require"
 
@@ -44,6 +45,7 @@ func TestInitialDeploy(t *testing.T) {
 		MCMSConfig:         ccdeploy.NewTestMCMSConfig(t, e),
 		CapabilityRegistry: state.Chains[tenv.HomeChainSel].CapabilityRegistry.Address(),
 		FeeTokenContracts:  tenv.FeeTokenContracts,
+		OCRSecrets:         deployment.XXXGenerateTestOCRSecrets(),
 	})
 	require.NoError(t, err)
 	// Get new state after migration.
diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go
index 2f7aa43245b..cd7464cbe5f 100644
--- a/integration-tests/deployment/ccip/deploy.go
+++ b/integration-tests/deployment/ccip/deploy.go
@@ -49,6 +49,7 @@ var (
 	CancellerManyChainMultisig deployment.ContractType = "CancellerManyChainMultiSig"
 	ProposerManyChainMultisig  deployment.ContractType = "ProposerManyChainMultiSig"
 	CCIPHome                   deployment.ContractType = "CCIPHome"
+	CCIPConfig                 deployment.ContractType = "CCIPConfig"
 	RMNHome                    deployment.ContractType = "RMNHome"
 	RBACTimelock               deployment.ContractType = "RBACTimelock"
 	OnRamp                     deployment.ContractType = "OnRamp"
@@ -126,12 +127,17 @@ type DeployCCIPContractConfig struct {
 	// I believe it makes sense to have the same signers across all chains
 	// since that's the point MCMS.
 	MCMSConfig MCMSConfig
+	// For setting OCR configuration
+	OCRSecrets deployment.OCRSecrets
 }
 
 // DeployCCIPContracts assumes that the capability registry and ccip home contracts
 // are already deployed (needed as a first step because the chainlink nodes point to them).
 // It then deploys
 func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c DeployCCIPContractConfig) error {
+	if c.OCRSecrets.IsEmpty() {
+		return fmt.Errorf("OCR secrets are empty")
+	}
 	nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain)
 	if err != nil || len(nodes) == 0 {
 		e.Logger.Errorw("Failed to get node info", "err", err)
@@ -226,6 +232,7 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c
 		// For each chain, we create a DON on the home chain (2 OCR instances)
 		if err := AddDON(
 			e.Logger,
+			c.OCRSecrets,
 			capReg,
 			ccipHome,
 			common.HexToAddress(rmnHomeAddress).Bytes(),
diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go
index b854078f79d..fa50a54878c 100644
--- a/integration-tests/deployment/ccip/deploy_home_chain.go
+++ b/integration-tests/deployment/ccip/deploy_home_chain.go
@@ -263,6 +263,7 @@ func AddChainConfig(
 
 func BuildAddDONArgs(
 	lggr logger.Logger,
+	ocrSecrets deployment.OCRSecrets,
 	offRamp *offramp.OffRamp,
 	dest deployment.Chain,
 	feedChainSel uint64,
@@ -317,7 +318,9 @@ func BuildAddDONArgs(
 		if err2 != nil {
 			return nil, err2
 		}
-		signers, transmitters, configF, _, offchainConfigVersion, offchainConfig, err2 := ocr3confighelper.ContractSetConfigArgsForTests(
+		signers, transmitters, configF, _, offchainConfigVersion, offchainConfig, err2 := ocr3confighelper.ContractSetConfigArgsDeterministic(
+			ocrSecrets.EphemeralSk,
+			ocrSecrets.SharedSecret,
 			DeltaProgress,
 			DeltaResend,
 			DeltaInitial,
@@ -766,6 +769,7 @@ func setupCommitDON(
 
 func AddDON(
 	lggr logger.Logger,
+	ocrSecrets deployment.OCRSecrets,
 	capReg *capabilities_registry.CapabilitiesRegistry,
 	ccipHome *ccip_home.CCIPHome,
 	rmnHomeAddress []byte,
@@ -777,7 +781,7 @@ func AddDON(
 	home deployment.Chain,
 	nodes deployment.Nodes,
 ) error {
-	ocrConfigs, err := BuildAddDONArgs(lggr, offRamp, dest, feedChainSel, tokenInfo, nodes, rmnHomeAddress)
+	ocrConfigs, err := BuildAddDONArgs(lggr, ocrSecrets, offRamp, dest, feedChainSel, tokenInfo, nodes, rmnHomeAddress)
 	if err != nil {
 		return err
 	}
diff --git a/integration-tests/deployment/ccip/deploy_test.go b/integration-tests/deployment/ccip/deploy_test.go
index 6305ac8e069..3742442433e 100644
--- a/integration-tests/deployment/ccip/deploy_test.go
+++ b/integration-tests/deployment/ccip/deploy_test.go
@@ -41,6 +41,7 @@ func TestDeployCCIPContracts(t *testing.T) {
 		CapabilityRegistry: s.Chains[homeChainSel].CapabilityRegistry.Address(),
 		FeeTokenContracts:  feeTokenContracts,
 		MCMSConfig:         NewTestMCMSConfig(t, e),
+		OCRSecrets:         deployment.XXXGenerateTestOCRSecrets(),
 	})
 	require.NoError(t, err)
 	state, err := LoadOnchainState(e, ab)
diff --git a/integration-tests/deployment/ccip/state.go b/integration-tests/deployment/ccip/state.go
index 4fc46c953b0..e4bbe9863a0 100644
--- a/integration-tests/deployment/ccip/state.go
+++ b/integration-tests/deployment/ccip/state.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/smartcontractkit/chainlink/integration-tests/deployment"
 	"github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip/view/v1_0"
+	"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config"
 	"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store"
 	"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home"
 
@@ -70,6 +71,8 @@ type CCIPChainState struct {
 	CancellerMcm       *owner_wrappers.ManyChainMultiSig
 	ProposerMcm        *owner_wrappers.ManyChainMultiSig
 	Timelock           *owner_wrappers.RBACTimelock
+	// TODO remove once staging upgraded.
+	CCIPConfig *ccip_config.CCIPConfig
 
 	// Test contracts
 	Receiver   *maybe_revert_message_receiver.MaybeRevertMessageReceiver
@@ -362,6 +365,13 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type
 				return state, err
 			}
 			state.CCIPHome = ccipHome
+		case deployment.NewTypeAndVersion(CCIPConfig, deployment.Version1_0_0).String():
+			// TODO: Remove once staging upgraded.
+			ccipConfig, err := ccip_config.NewCCIPConfig(common.HexToAddress(address), chain.Client)
+			if err != nil {
+				return state, err
+			}
+			state.CCIPConfig = ccipConfig
 		case deployment.NewTypeAndVersion(CCIPReceiver, deployment.Version1_0_0).String():
 			mr, err := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(common.HexToAddress(address), chain.Client)
 			if err != nil {
diff --git a/integration-tests/deployment/environment.go b/integration-tests/deployment/environment.go
index 163a29bf5b4..0b723a358d6 100644
--- a/integration-tests/deployment/environment.go
+++ b/integration-tests/deployment/environment.go
@@ -33,6 +33,7 @@ type OnchainClient interface {
 	bind.ContractBackend
 	bind.DeployBackend
 	BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
+	NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
 }
 
 type OffchainClient interface {
@@ -96,7 +97,7 @@ func ConfirmIfNoError(chain Chain, tx *types.Transaction, err error) (uint64, er
 		var d rpc.DataError
 		ok := errors.As(err, &d)
 		if ok {
-			return 0, fmt.Errorf("got Data Error: %s", d.ErrorData())
+			return 0, fmt.Errorf("transaction reverted: Error %s ErrorData %v", d.Error(), d.ErrorData())
 		}
 		return 0, err
 	}
diff --git a/integration-tests/deployment/helpers.go b/integration-tests/deployment/helpers.go
index c691d1a1ec1..226de9a7024 100644
--- a/integration-tests/deployment/helpers.go
+++ b/integration-tests/deployment/helpers.go
@@ -13,9 +13,30 @@ import (
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/pkg/errors"
 )
 
+// OCRSecrets are used to disseminate a shared secret to OCR nodes
+// through the blockchain where OCR configuration is stored. Its a low value secret used
+// to derive transmission order etc. They are extracted here such that they can common
+// across signers when multiple signers are signing the same OCR config.
+type OCRSecrets struct {
+	SharedSecret [16]byte
+	EphemeralSk  [32]byte
+}
+
+func (s OCRSecrets) IsEmpty() bool {
+	return s.SharedSecret == [16]byte{} || s.EphemeralSk == [32]byte{}
+}
+
+func XXXGenerateTestOCRSecrets() OCRSecrets {
+	var s OCRSecrets
+	copy(s.SharedSecret[:], crypto.Keccak256([]byte("shared"))[:16])
+	copy(s.EphemeralSk[:], crypto.Keccak256([]byte("ephemeral")))
+	return s
+}
+
 // SimTransactOpts is useful to generate just the calldata for a given gethwrapper method.
 func SimTransactOpts() *bind.TransactOpts {
 	return &bind.TransactOpts{Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
diff --git a/integration-tests/deployment/multiclient.go b/integration-tests/deployment/multiclient.go
index 94dcc95f9ec..65f9e82bd01 100644
--- a/integration-tests/deployment/multiclient.go
+++ b/integration-tests/deployment/multiclient.go
@@ -87,11 +87,11 @@ func (mc *MultiClient) CodeAt(ctx context.Context, account common.Address, block
 	return code, err
 }
 
-func (mc *MultiClient) NonceAt(ctx context.Context, account common.Address) (uint64, error) {
+func (mc *MultiClient) NonceAt(ctx context.Context, account common.Address, block *big.Int) (uint64, error) {
 	var count uint64
 	err := mc.retryWithBackups("NonceAt", func(client *ethclient.Client) error {
 		var err error
-		count, err = client.NonceAt(ctx, account, nil)
+		count, err = client.NonceAt(ctx, account, block)
 		return err
 	})
 	return count, err
diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go
index 565df6a2dd9..b34876c5b2b 100644
--- a/integration-tests/smoke/ccip_test.go
+++ b/integration-tests/smoke/ccip_test.go
@@ -6,6 +6,8 @@ import (
 	"github.com/stretchr/testify/require"
 
 	cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
+	"github.com/smartcontractkit/chainlink/integration-tests/deployment"
+
 	"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
 
 	"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
@@ -43,6 +45,7 @@ func TestInitialDeployOnLocal(t *testing.T) {
 		MCMSConfig:         ccdeploy.NewTestMCMSConfig(t, e),
 		CapabilityRegistry: state.Chains[tenv.HomeChainSel].CapabilityRegistry.Address(),
 		FeeTokenContracts:  tenv.FeeTokenContracts,
+		OCRSecrets:         deployment.XXXGenerateTestOCRSecrets(),
 	})
 	require.NoError(t, err)
 	// Get new state after migration.